diff --git a/cmd/main.go b/cmd/main.go index 36861b8..4e9eed3 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -10,10 +10,13 @@ import ( "time" "github.com/go-playground/validator/v10" + "go.uber.org/zap" + // "github.com/gofiber/fiber/v2" "github.com/SamuelTariku/FortuneBet-Backend/internal/config" 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" @@ -80,7 +83,8 @@ func main() { logger := customlogger.NewLogger(cfg.Env, cfg.LogLevel) - // mongologger.Init() + mongoLogger.Init() + mongoDBLogger := zap.L() // client := mongoLogger.InitDB() // defer func() { @@ -139,7 +143,7 @@ func main() { branchSvc := branch.NewService(store) companySvc := company.NewService(store) leagueSvc := league.New(store) - betSvc := bet.NewService(store, eventSvc, oddsSvc, *walletSvc, *branchSvc, logger) + betSvc := bet.NewService(store, eventSvc, oddsSvc, *walletSvc, *branchSvc, logger, mongoDBLogger) resultSvc := result.NewService(store, cfg, logger, *betSvc) referalRepo := repository.NewReferralRepository(store) vitualGameRepo := repository.NewVirtualGameRepository(store) diff --git a/internal/logger/mongoLogger/init.go b/internal/logger/mongoLogger/init.go index b71eb97..b7b333d 100644 --- a/internal/logger/mongoLogger/init.go +++ b/internal/logger/mongoLogger/init.go @@ -25,6 +25,6 @@ func Init() { defer logger.Sync() - logger.Info("Application started", zap.String("module", "main")) - logger.Error("Something went wrong", zap.String("error_code", "E123")) + // logger.Info("Application started", zap.String("module", "main")) + // logger.Error("Something went wrong", zap.String("error_code", "E123")) } diff --git a/internal/repository/bet.go b/internal/repository/bet.go index 23e97b7..32cd4ac 100644 --- a/internal/repository/bet.go +++ b/internal/repository/bet.go @@ -11,9 +11,13 @@ import ( 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" ) -var logger *slog.Logger +var ( + logger *slog.Logger + mongoLogger *zap.Logger +) func convertDBBet(bet dbgen.Bet) domain.Bet { return domain.Bet{ @@ -151,9 +155,14 @@ func (s *Store) CreateBetOutcome(ctx context.Context, outcomes []domain.CreateBe for _, outcome := range outcomes { dbParams = append(dbParams, convertDBCreateBetOutcome(outcome)) } - rows, err := s.queries.CreateBetOutcome(ctx, dbParams) + rows, err := s.queries.CreateBetOutcome(ctx, dbParams) if err != nil { + mongoLogger.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 } @@ -162,8 +171,11 @@ func (s *Store) CreateBetOutcome(ctx context.Context, outcomes []domain.CreateBe func (s *Store) GetBetByID(ctx context.Context, id int64) (domain.GetBet, error) { bet, err := s.queries.GetBetByID(ctx, id) - if err != nil { + mongoLogger.Error("failed to get bet by ID", + zap.Int64("bet_id", id), + zap.Error(err), + ) return domain.GetBet{}, err } @@ -172,8 +184,11 @@ func (s *Store) GetBetByID(ctx context.Context, id int64) (domain.GetBet, error) func (s *Store) GetBetByCashoutID(ctx context.Context, id string) (domain.GetBet, error) { bet, err := s.queries.GetBetByCashoutID(ctx, id) - if err != nil { + mongoLogger.Error("failed to get bet by cashout ID", + zap.String("cashout_id", id), + zap.Error(err), + ) return domain.GetBet{}, err } @@ -196,6 +211,10 @@ func (s *Store) GetAllBets(ctx context.Context, filter domain.BetFilter) ([]doma }, }) if err != nil { + mongoLogger.Error("failed to get all bets", + zap.Any("filter", filter), + zap.Error(err), + ) return nil, err } @@ -212,8 +231,11 @@ func (s *Store) GetBetByBranchID(ctx context.Context, BranchID int64) ([]domain. Int64: BranchID, Valid: true, }) - if err != nil { + mongoLogger.Error("failed to get bets by branch ID", + zap.Int64("branch_id", BranchID), + zap.Error(err), + ) return nil, err } @@ -248,6 +270,13 @@ func (s *Store) UpdateCashOut(ctx context.Context, id int64, cashedOut bool) err ID: id, CashedOut: cashedOut, }) + if err != nil { + mongoLogger.Error("failed to update cashout", + zap.Int64("id", id), + zap.Bool("cashed_out", cashedOut), + zap.Error(err), + ) + } return err } @@ -256,16 +285,27 @@ func (s *Store) UpdateStatus(ctx context.Context, id int64, status domain.Outcom ID: id, Status: int32(status), }) + if err != nil { + mongoLogger.Error("failed to update status", + zap.Int64("id", id), + zap.Int32("status", int32(status)), + zap.Error(err), + ) + } return err } func (s *Store) GetBetOutcomeByEventID(ctx context.Context, eventID int64) ([]domain.BetOutcome, error) { outcomes, err := s.queries.GetBetOutcomeByEventID(ctx, eventID) if err != nil { - return nil, nil + mongoLogger.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)) + var result []domain.BetOutcome = make([]domain.BetOutcome, 0, len(outcomes)) for _, outcome := range outcomes { result = append(result, convertDBBetOutcomes(outcome)) } @@ -275,22 +315,36 @@ func (s *Store) GetBetOutcomeByEventID(ctx context.Context, eventID int64) ([]do func (s *Store) GetBetOutcomeByBetID(ctx context.Context, betID int64) ([]domain.BetOutcome, error) { outcomes, err := s.queries.GetBetOutcomeByBetID(ctx, betID) if err != nil { - return nil, nil + mongoLogger.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)) + var result []domain.BetOutcome = make([]domain.BetOutcome, 0, len(outcomes)) for _, outcome := range outcomes { result = append(result, 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 { + mongoLogger.Error("failed to update bet outcome status", + zap.Int64("id", id), + zap.Int32("status", int32(status)), + zap.Error(err), + ) + return domain.BetOutcome{}, err + } + res := convertDBBetOutcomes(update) - return res, err + return res, nil } func (s *Store) DeleteBet(ctx context.Context, id int64) error { @@ -374,9 +428,24 @@ func (s *Store) GetBetSummary(ctx context.Context, filter domain.ReportFilter) ( row := s.conn.QueryRow(ctx, query, args...) err = row.Scan(&totalStakes, &totalBets, &activeBets, &totalWins, &totalLosses, &winBalance) if err != nil { + mongoLogger.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) } + mongoLogger.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 } @@ -450,6 +519,11 @@ func (s *Store) GetBetStats(ctx context.Context, filter domain.ReportFilter) ([] rows, err := s.conn.Query(ctx, query, args...) if err != nil { + mongoLogger.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() @@ -465,15 +539,26 @@ func (s *Store) GetBetStats(ctx context.Context, filter domain.ReportFilter) ([] &stat.TotalPayouts, &stat.AverageOdds, ); err != nil { + mongoLogger.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 { + mongoLogger.Error("rows error after iteration", + zap.Error(err), + ) return nil, fmt.Errorf("rows error: %w", err) } + mongoLogger.Info("GetBetStats executed successfully", + zap.Int("result_count", len(stats)), + zap.String("query", query), + zap.Any("args", args), + ) return stats, nil } @@ -530,6 +615,11 @@ func (s *Store) GetSportPopularity(ctx context.Context, filter domain.ReportFilt rows, err := s.conn.Query(ctx, query, args...) if err != nil { + mongoLogger.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() @@ -539,15 +629,26 @@ func (s *Store) GetSportPopularity(ctx context.Context, filter domain.ReportFilt var date time.Time var sportID string if err := rows.Scan(&date, &sportID); err != nil { + mongoLogger.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 { + mongoLogger.Error("rows error after iteration", + zap.Error(err), + ) return nil, fmt.Errorf("rows error: %w", err) } + mongoLogger.Info("GetSportPopularity executed successfully", + zap.Int("result_count", len(popularity)), + zap.String("query", query), + zap.Any("args", args), + ) return popularity, nil } @@ -604,6 +705,11 @@ func (s *Store) GetMarketPopularity(ctx context.Context, filter domain.ReportFil rows, err := s.conn.Query(ctx, query, args...) if err != nil { + mongoLogger.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() @@ -613,15 +719,26 @@ func (s *Store) GetMarketPopularity(ctx context.Context, filter domain.ReportFil var date time.Time var marketName string if err := rows.Scan(&date, &marketName); err != nil { + mongoLogger.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 { + mongoLogger.Error("rows error after iteration", + zap.Error(err), + ) return nil, fmt.Errorf("rows error: %w", err) } + mongoLogger.Info("GetMarketPopularity executed successfully", + zap.Int("result_count", len(popularity)), + zap.String("query", query), + zap.Any("args", args), + ) return popularity, nil } @@ -692,6 +809,11 @@ func (s *Store) GetExtremeValues(ctx context.Context, filter domain.ReportFilter rows, err := s.conn.Query(ctx, query, args...) if err != nil { + mongoLogger.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() @@ -701,15 +823,26 @@ func (s *Store) GetExtremeValues(ctx context.Context, filter domain.ReportFilter var date time.Time var extreme domain.ExtremeValues if err := rows.Scan(&date, &extreme.HighestStake, &extreme.HighestPayout); err != nil { + mongoLogger.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 { + mongoLogger.Error("rows error after iteration", + zap.Error(err), + ) return nil, fmt.Errorf("rows error: %w", err) } + mongoLogger.Info("GetExtremeValues executed successfully", + zap.Int("result_count", len(extremes)), + zap.String("query", query), + zap.Any("args", args), + ) return extremes, nil } @@ -766,6 +899,11 @@ func (s *Store) GetCustomerBetActivity(ctx context.Context, filter domain.Report rows, err := s.conn.Query(ctx, query, args...) if err != nil { + mongoLogger.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() @@ -783,15 +921,26 @@ func (s *Store) GetCustomerBetActivity(ctx context.Context, filter domain.Report &activity.LastBetDate, &activity.AverageOdds, ); err != nil { + mongoLogger.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 { + mongoLogger.Error("rows error after iteration", + zap.Error(err), + ) return nil, fmt.Errorf("rows error: %w", err) } + mongoLogger.Info("GetCustomerBetActivity executed successfully", + zap.Int("result_count", len(activities)), + zap.String("query", query), + zap.Any("args", args), + ) return activities, nil } @@ -840,6 +989,11 @@ func (s *Store) GetBranchBetActivity(ctx context.Context, filter domain.ReportFi rows, err := s.conn.Query(ctx, query, args...) if err != nil { + mongoLogger.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() @@ -854,15 +1008,22 @@ func (s *Store) GetBranchBetActivity(ctx context.Context, filter domain.ReportFi &activity.TotalWins, &activity.TotalPayouts, ); err != nil { + mongoLogger.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 { + mongoLogger.Error("rows error after iteration", zap.Error(err)) return nil, fmt.Errorf("rows error: %w", err) } + mongoLogger.Info("GetBranchBetActivity executed successfully", + zap.Int("result_count", len(activities)), + zap.String("query", query), + zap.Any("args", args), + ) return activities, nil } @@ -882,7 +1043,6 @@ func (s *Store) GetSportBetActivity(ctx context.Context, filter domain.ReportFil 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) @@ -918,6 +1078,11 @@ func (s *Store) GetSportBetActivity(ctx context.Context, filter domain.ReportFil rows, err := s.conn.Query(ctx, query, args...) if err != nil { + mongoLogger.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() @@ -933,15 +1098,22 @@ func (s *Store) GetSportBetActivity(ctx context.Context, filter domain.ReportFil &activity.TotalPayouts, &activity.AverageOdds, ); err != nil { + mongoLogger.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 { + mongoLogger.Error("rows error after iteration", zap.Error(err)) return nil, fmt.Errorf("rows error: %w", err) } + mongoLogger.Info("GetSportBetActivity executed successfully", + zap.Int("result_count", len(activities)), + zap.String("query", query), + zap.Any("args", args), + ) return activities, nil } @@ -950,12 +1122,27 @@ func (s *Store) GetSportDetails(ctx context.Context, filter domain.ReportFilter) 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 - // 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 bo.created_at >= $%d", argPos) args = append(args, filter.StartTime.Value) @@ -969,6 +1156,11 @@ func (s *Store) GetSportDetails(ctx context.Context, filter domain.ReportFilter) rows, err := s.conn.Query(ctx, query, args...) if err != nil { + mongoLogger.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() @@ -977,15 +1169,23 @@ func (s *Store) GetSportDetails(ctx context.Context, filter domain.ReportFilter) for rows.Next() { var sportID, matchName string if err := rows.Scan(&sportID, &matchName); err != nil { + mongoLogger.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 { + mongoLogger.Error("rows error after iteration", zap.Error(err)) return nil, fmt.Errorf("rows error: %w", err) } + mongoLogger.Info("GetSportDetails executed successfully", + zap.Int("result_count", len(details)), + zap.String("query", query), + zap.Any("args", args), + ) + return details, nil } @@ -995,7 +1195,7 @@ func (s *Store) GetSportMarketPopularity(ctx context.Context, filter domain.Repo SELECT bo.sport_id, bo.market_name, - COUNT(*) as bet_count, + 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 @@ -1004,7 +1204,6 @@ func (s *Store) GetSportMarketPopularity(ctx context.Context, filter domain.Repo 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) @@ -1042,6 +1241,11 @@ func (s *Store) GetSportMarketPopularity(ctx context.Context, filter domain.Repo rows, err := s.conn.Query(ctx, query, args...) if err != nil { + mongoLogger.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() @@ -1050,14 +1254,22 @@ func (s *Store) GetSportMarketPopularity(ctx context.Context, filter domain.Repo for rows.Next() { var sportID, marketName string if err := rows.Scan(&sportID, &marketName); err != nil { + mongoLogger.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 { + mongoLogger.Error("rows error after iteration", zap.Error(err)) return nil, fmt.Errorf("rows error: %w", err) } + mongoLogger.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/services/bet/service.go b/internal/services/bet/service.go index 6e49580..f0d6236 100644 --- a/internal/services/bet/service.go +++ b/internal/services/bet/service.go @@ -17,6 +17,7 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" + "go.uber.org/zap" ) var ( @@ -33,9 +34,10 @@ type Service struct { walletSvc wallet.Service branchSvc branch.Service logger *slog.Logger + mongoLogger *zap.Logger } -func NewService(betStore BetStore, eventSvc event.Service, prematchSvc odds.Service, walletSvc wallet.Service, branchSvc branch.Service, logger *slog.Logger) *Service { +func NewService(betStore BetStore, eventSvc event.Service, prematchSvc odds.Service, walletSvc wallet.Service, branchSvc branch.Service, logger *slog.Logger, mongoLogger *zap.Logger) *Service { return &Service{ betStore: betStore, eventSvc: eventSvc, @@ -43,6 +45,7 @@ func NewService(betStore BetStore, eventSvc event.Service, prematchSvc odds.Serv walletSvc: walletSvc, branchSvc: branchSvc, logger: logger, + mongoLogger: mongoLogger, } } @@ -58,37 +61,56 @@ func (s *Service) GenerateCashoutID() (string, error) { 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) GenerateBetOutcome(ctx context.Context, eventID int64, marketID int64, oddID int64) (domain.CreateBetOutcome, error) { - // TODO: Change this when you refactor the database code eventIDStr := strconv.FormatInt(eventID, 10) marketIDStr := strconv.FormatInt(marketID, 10) oddIDStr := strconv.FormatInt(oddID, 10) event, err := s.eventSvc.GetUpcomingEventByID(ctx, eventIDStr) 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 } 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 } odds, err := s.prematchSvc.GetRawOddsByMarketID(ctx, marketIDStr, eventIDStr) - 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 } + type rawOddType struct { ID string Name string @@ -98,29 +120,51 @@ func (s *Service) GenerateBetOutcome(ctx context.Context, eventID int64, marketI } var selectedOdd rawOddType - var isOddFound bool = false + 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 { - fmt.Printf("Failed to unmarshal raw odd %v", err) + 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, @@ -137,13 +181,14 @@ func (s *Service) GenerateBetOutcome(ctx context.Context, eventID int64, marketI } return newOutcome, nil - } func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID int64, role domain.Role) (domain.CreateBetRes, error) { - // You can move the loop over req.Outcomes and all the business logic here. - if len(req.Outcomes) > 30 { + s.mongoLogger.Error("too many outcomes", + zap.Int("count", len(req.Outcomes)), + zap.Int64("user_id", userID), + ) return domain.CreateBetRes{}, ErrOutcomeLimit } @@ -153,17 +198,25 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID for _, outcomeReq := range req.Outcomes { newOutcome, err := s.GenerateBetOutcome(ctx, 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.Int64("user_id", userID), + zap.Error(err), + ) return domain.CreateBetRes{}, err } - totalOdds = totalOdds * float32(newOutcome.Odd) + totalOdds *= float32(newOutcome.Odd) outcomes = append(outcomes, newOutcome) } - // Handle role-specific logic and wallet deduction if needed. - var cashoutID string cashoutID, err := s.GenerateCashoutID() - if err != nil { + s.mongoLogger.Error("failed to generate cashout ID", + zap.Int64("user_id", userID), + zap.Error(err), + ) return domain.CreateBetRes{}, err } @@ -175,106 +228,117 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID PhoneNumber: req.PhoneNumber, CashoutID: cashoutID, } + switch role { case domain.RoleCashier: 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 } - // Deduct from wallet: - // TODO: Make this percentage come from the company - var deductedAmount = req.Amount / 10 + + deductedAmount := req.Amount / 10 err = s.walletSvc.DeductFromWallet(ctx, branch.WalletID, domain.ToCurrency(deductedAmount)) - if err != nil { + s.mongoLogger.Error("failed to deduct from wallet", + zap.Int64("wallet_id", branch.WalletID), + zap.Float32("amount", deductedAmount), + zap.Error(err), + ) return domain.CreateBetRes{}, err } - newBet.BranchID = domain.ValidInt64{ - Value: branch.ID, - Valid: true, - } - newBet.CompanyID = domain.ValidInt64{ - Value: branch.CompanyID, - Valid: true, - } - newBet.UserID = domain.ValidInt64{ - Value: userID, - Valid: true, - } + newBet.BranchID = domain.ValidInt64{Value: branch.ID, Valid: true} + newBet.CompanyID = domain.ValidInt64{Value: branch.CompanyID, Valid: true} + newBet.UserID = domain.ValidInt64{Value: userID, Valid: true} newBet.IsShopBet = true - // bet, err = s.betStore.CreateBet(ctx) + case domain.RoleBranchManager, domain.RoleAdmin, domain.RoleSuperAdmin: - // TODO: restrict the Branch ID of Admin and Branch Manager to only the branches within their own company - // If a non cashier wants to create a bet, they will need to provide the Branch ID if req.BranchID == nil { + s.mongoLogger.Error("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 } - // Deduct from wallet: - // TODO: Make this percentage come from the company - var deductedAmount = req.Amount / 10 + + deductedAmount := req.Amount / 10 err = s.walletSvc.DeductFromWallet(ctx, branch.WalletID, domain.ToCurrency(deductedAmount)) - if err != nil { + s.mongoLogger.Error("wallet deduction failed", + zap.Int64("wallet_id", branch.WalletID), + zap.Float32("amount", deductedAmount), + zap.Error(err), + ) return domain.CreateBetRes{}, err } - newBet.BranchID = domain.ValidInt64{ - Value: branch.ID, - Valid: true, - } - newBet.CompanyID = domain.ValidInt64{ - Value: branch.CompanyID, - Valid: true, - } - newBet.UserID = domain.ValidInt64{ - Value: userID, - Valid: true, - } + newBet.BranchID = domain.ValidInt64{Value: branch.ID, Valid: true} + newBet.CompanyID = domain.ValidInt64{Value: branch.CompanyID, Valid: true} + newBet.UserID = domain.ValidInt64{Value: userID, Valid: true} newBet.IsShopBet = true + case domain.RoleCustomer: - // Get User Wallet - - wallet, err := s.walletSvc.GetWalletsByUser(ctx, userID) - + wallets, err := s.walletSvc.GetWalletsByUser(ctx, userID) if err != nil { + s.mongoLogger.Error("failed to get customer wallets", + zap.Int64("user_id", userID), + zap.Error(err), + ) return domain.CreateBetRes{}, err } - userWallet := wallet[0] - + userWallet := wallets[0] err = s.walletSvc.DeductFromWallet(ctx, userWallet.ID, domain.ToCurrency(req.Amount)) if err != nil { + s.mongoLogger.Error("wallet deduction failed for customer", + zap.Int64("wallet_id", userWallet.ID), + zap.Float32("amount", req.Amount), + zap.Error(err), + ) return domain.CreateBetRes{}, err } - newBet.UserID = domain.ValidInt64{ - Value: userID, - Valid: true, - } + newBet.UserID = domain.ValidInt64{Value: userID, Valid: true} newBet.IsShopBet = false 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 } - // Associate outcomes with the bet. 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 } @@ -289,14 +353,24 @@ func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID string, var totalOdds float32 = 1 markets, err := s.prematchSvc.GetPrematchOddsByUpcomingID(ctx, eventID) - if err != nil { s.logger.Error("failed to get odds for event", "event id", eventID, "error", err) + s.mongoLogger.Error("failed to get odds for event", + zap.String("eventID", eventID), + zap.Int32("sportID", sportID), + zap.String("homeTeam", HomeTeam), + zap.String("awayTeam", AwayTeam), + zap.Error(err)) return nil, 0, err } if len(markets) == 0 { s.logger.Error("empty odds for event", "event id", eventID) + s.mongoLogger.Warn("empty odds for event", + zap.String("eventID", eventID), + zap.Int32("sportID", sportID), + zap.String("homeTeam", HomeTeam), + zap.String("awayTeam", AwayTeam)) return nil, 0, fmt.Errorf("empty odds or event %v", eventID) } @@ -325,35 +399,55 @@ func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID string, err = json.Unmarshal(rawBytes, &selectedOdd) if err != nil { - fmt.Printf("Failed to unmarshal raw odd %v", err) + s.logger.Error("Failed to unmarshal raw odd", "error", err) + s.mongoLogger.Warn("Failed to unmarshal raw odd", + zap.String("eventID", eventID), + zap.Int32("sportID", sportID), + zap.Error(err)) continue } + parsedOdd, err := strconv.ParseFloat(selectedOdd.Odds, 32) if err != nil { s.logger.Error("Failed to parse odd", "error", err) + s.mongoLogger.Warn("Failed to parse odd", + zap.String("eventID", eventID), + zap.String("oddValue", selectedOdd.Odds), + zap.Error(err)) continue } - eventID, err := strconv.ParseInt(eventID, 10, 64) + + eventIDInt, err := strconv.ParseInt(eventID, 10, 64) if err != nil { - s.logger.Error("Failed to get event id", "error", err) + s.logger.Error("Failed to parse eventID", "error", err) + s.mongoLogger.Warn("Failed to parse eventID", + zap.String("eventID", eventID), + zap.Error(err)) continue } + oddID, err := strconv.ParseInt(selectedOdd.ID, 10, 64) if err != nil { - s.logger.Error("Failed to get odd id", "error", err) + s.logger.Error("Failed to parse oddID", "error", err) + s.mongoLogger.Warn("Failed to parse oddID", + zap.String("oddID", selectedOdd.ID), + zap.Error(err)) continue } marketID, err := strconv.ParseInt(market.MarketID, 10, 64) if err != nil { - s.logger.Error("Failed to get odd id", "error", err) + s.logger.Error("Failed to parse marketID", "error", err) + s.mongoLogger.Warn("Failed to parse marketID", + zap.String("marketID", market.MarketID), + zap.Error(err)) continue } marketName := market.MarketName newOdds = append(newOdds, domain.CreateBetOutcome{ - EventID: eventID, + EventID: eventIDInt, OddID: oddID, MarketID: marketID, SportID: int64(sportID), @@ -367,15 +461,27 @@ func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID string, Expires: StartTime, }) - totalOdds = totalOdds * float32(parsedOdd) - + totalOdds *= float32(parsedOdd) } if len(newOdds) == 0 { - s.logger.Error("Bet Outcomes is empty for market", "selectedMarket", selectedMarkets[0].MarketName) + s.logger.Error("Bet Outcomes is empty for market", "selectedMarkets", len(selectedMarkets)) + s.mongoLogger.Error("Bet Outcomes is empty for market", + zap.String("eventID", eventID), + zap.Int32("sportID", sportID), + zap.String("homeTeam", HomeTeam), + zap.String("awayTeam", AwayTeam), + zap.Int("selectedMarkets", len(selectedMarkets))) return nil, 0, ErrGenerateRandomOutcome } + // ✅ Final success log (optional) + s.mongoLogger.Info("Random bet outcomes generated successfully", + zap.String("eventID", eventID), + zap.Int32("sportID", sportID), + zap.Int("numOutcomes", len(newOdds)), + zap.Float32("totalOdds", totalOdds)) + return newOdds, totalOdds, nil } @@ -387,10 +493,17 @@ func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID int64, le domain.ValidInt64{}, domain.ValidInt64{}, leagueID, sportID, firstStartTime, lastStartTime) if err != nil { + s.mongoLogger.Error("failed to get paginated upcoming events", + zap.Int64("userID", userID), + zap.Int64("branchID", branchID), + zap.Error(err)) return domain.CreateBetRes{}, err } if len(events) == 0 { + s.mongoLogger.Warn("no events available for random bet", + zap.Int64("userID", userID), + zap.Int64("branchID", branchID)) return domain.CreateBetRes{}, ErrNoEventsAvailable } @@ -417,6 +530,11 @@ func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID int64, le if err != nil { s.logger.Error("failed to generate random bet outcome", "event id", event.ID, "error", err) + s.mongoLogger.Error("failed to generate random bet outcome", + zap.Int64("userID", userID), + zap.Int64("branchID", branchID), + zap.String("eventID", event.ID), + zap.String("error", fmt.Sprintf("%v", err))) continue } @@ -426,6 +544,9 @@ func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID int64, le } if len(randomOdds) == 0 { s.logger.Error("Failed to generate random any outcomes for all events") + s.mongoLogger.Error("Failed to generate random any outcomes for all events", + zap.Int64("userID", userID), + zap.Int64("branchID", branchID)) return domain.CreateBetRes{}, ErrGenerateRandomOutcome } @@ -435,6 +556,9 @@ func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID int64, le cashoutID, err = s.GenerateCashoutID() if err != nil { + s.mongoLogger.Error("Failed to generate cash out ID", + zap.Int64("userID", userID), + zap.Int64("branchID", branchID)) return domain.CreateBetRes{}, err } @@ -452,6 +576,10 @@ func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID int64, le bet, err := s.CreateBet(ctx, newBet) if err != nil { + s.mongoLogger.Error("Failed to create a new random bet", + zap.Int64("userID", userID), + zap.Int64("branchID", branchID), + zap.String("bet", fmt.Sprintf("%+v", newBet))) return domain.CreateBetRes{}, err } @@ -461,11 +589,19 @@ func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID int64, le rows, err := s.betStore.CreateBetOutcome(ctx, randomOdds) if err != nil { + s.mongoLogger.Error("Failed to create a new random bet outcome", + zap.Int64("userID", userID), + zap.Int64("branchID", branchID), + zap.String("randomOdds", fmt.Sprintf("%+v", randomOdds))) return domain.CreateBetRes{}, err } res := domain.ConvertCreateBet(bet, rows) + s.mongoLogger.Info("Random bets placed successfully", + zap.Int64("userID", userID), + zap.Int64("branchID", branchID), + zap.String("response", fmt.Sprintf("%+v", res))) return res, nil } @@ -500,10 +636,12 @@ func (s *Service) UpdateCashOut(ctx context.Context, id int64, cashedOut bool) e } func (s *Service) UpdateStatus(ctx context.Context, id int64, status domain.OutcomeStatus) error { - bet, err := s.GetBetByID(ctx, id) if err != nil { - s.logger.Error("Failed to update bet status. Invalid bet id") + s.mongoLogger.Error("failed to update bet status: invalid bet ID", + zap.Int64("bet_id", id), + zap.Error(err), + ) return err } @@ -516,22 +654,30 @@ func (s *Service) UpdateStatus(ctx context.Context, id int64, status domain.Outc customerWallet, err := s.walletSvc.GetCustomerWallet(ctx, id) if err != nil { - s.logger.Error("Failed to update bet status. Invalid customer wallet id") + s.mongoLogger.Error("failed to get customer wallet", + zap.Int64("bet_id", id), + zap.Error(err), + ) return err } var amount domain.Currency - if status == domain.OUTCOME_STATUS_WIN { + switch status { + case domain.OUTCOME_STATUS_WIN: amount = domain.CalculateWinnings(bet.Amount, bet.TotalOdds) - } else if status == domain.OUTCOME_STATUS_HALF { - amount = (domain.CalculateWinnings(bet.Amount, bet.TotalOdds)) / 2 - } else { + case domain.OUTCOME_STATUS_HALF: + amount = domain.CalculateWinnings(bet.Amount, bet.TotalOdds) / 2 + default: amount = bet.Amount } - err = s.walletSvc.AddToWallet(ctx, customerWallet.RegularID, amount) + err = s.walletSvc.AddToWallet(ctx, customerWallet.RegularID, amount) if err != nil { - s.logger.Error("Failed to update bet status. Failed to update user wallet") + s.mongoLogger.Error("failed to add winnings to wallet", + zap.Int64("wallet_id", customerWallet.RegularID), + zap.Float32("amount", float32(amount)), + zap.Error(err), + ) return err } @@ -541,92 +687,89 @@ func (s *Service) UpdateStatus(ctx context.Context, id int64, status domain.Outc 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 any of the bet outcomes are pending return 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 } - // The bet status can only be updated if its not lost or error - // If all the bet outcomes are a win, then set the bet status to win - // If even one of the bet outcomes is a loss then set the bet status to loss - // If even one of the bet outcomes is an error, then set the bet status to error switch status { case domain.OUTCOME_STATUS_PENDING: status = betOutcome.Status case domain.OUTCOME_STATUS_WIN: - if betOutcome.Status == domain.OUTCOME_STATUS_LOSS { + switch betOutcome.Status { + case domain.OUTCOME_STATUS_LOSS: status = domain.OUTCOME_STATUS_LOSS - } else if betOutcome.Status == domain.OUTCOME_STATUS_HALF { + case domain.OUTCOME_STATUS_HALF: status = domain.OUTCOME_STATUS_HALF - } else if betOutcome.Status == domain.OUTCOME_STATUS_WIN { - status = domain.OUTCOME_STATUS_WIN - } else if betOutcome.Status == domain.OUTCOME_STATUS_VOID { + case domain.OUTCOME_STATUS_VOID: status = domain.OUTCOME_STATUS_VOID - } else { + case domain.OUTCOME_STATUS_WIN: + // remain win + default: status = domain.OUTCOME_STATUS_ERROR } case domain.OUTCOME_STATUS_LOSS: - if betOutcome.Status == domain.OUTCOME_STATUS_LOSS { - status = domain.OUTCOME_STATUS_LOSS - } else if betOutcome.Status == domain.OUTCOME_STATUS_HALF { - status = domain.OUTCOME_STATUS_LOSS - } else if betOutcome.Status == domain.OUTCOME_STATUS_WIN { - status = domain.OUTCOME_STATUS_LOSS - } else if betOutcome.Status == domain.OUTCOME_STATUS_VOID { - status = domain.OUTCOME_STATUS_LOSS - } else { - status = domain.OUTCOME_STATUS_ERROR - } + // stay as LOSS regardless of others case domain.OUTCOME_STATUS_VOID: - if betOutcome.Status == domain.OUTCOME_STATUS_VOID || - betOutcome.Status == domain.OUTCOME_STATUS_WIN || - betOutcome.Status == domain.OUTCOME_STATUS_HALF { - status = domain.OUTCOME_STATUS_VOID - } else if betOutcome.Status == domain.OUTCOME_STATUS_LOSS { + switch betOutcome.Status { + case domain.OUTCOME_STATUS_LOSS: status = domain.OUTCOME_STATUS_LOSS - - } else { + 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: - if betOutcome.Status == domain.OUTCOME_STATUS_HALF || - betOutcome.Status == domain.OUTCOME_STATUS_WIN { - status = domain.OUTCOME_STATUS_HALF - } else if betOutcome.Status == domain.OUTCOME_STATUS_LOSS { + switch betOutcome.Status { + case domain.OUTCOME_STATUS_LOSS: status = domain.OUTCOME_STATUS_LOSS - } else if betOutcome.Status == domain.OUTCOME_STATUS_VOID { + case domain.OUTCOME_STATUS_VOID: status = domain.OUTCOME_STATUS_VOID - } else { + case domain.OUTCOME_STATUS_HALF, domain.OUTCOME_STATUS_WIN: + // remain HALF + default: status = domain.OUTCOME_STATUS_ERROR } default: - // If the status is not pending, win, loss or error, then set the status to error status = domain.OUTCOME_STATUS_ERROR } } if status == domain.OUTCOME_STATUS_PENDING || status == domain.OUTCOME_STATUS_ERROR { - // If the status is pending or error, then we don't need to update the bet - s.logger.Info("bet not updated", "bet id", betID, "status", status) - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("Error when processing bet outcomes") + 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 } diff --git a/internal/logger/mongoLogger/handler.go b/internal/web_server/handlers/mongoLogger.go similarity index 98% rename from internal/logger/mongoLogger/handler.go rename to internal/web_server/handlers/mongoLogger.go index 0aeb15f..9f01297 100644 --- a/internal/logger/mongoLogger/handler.go +++ b/internal/web_server/handlers/mongoLogger.go @@ -1,4 +1,4 @@ -package mongoLogger +package handlers import ( "context" diff --git a/internal/web_server/handlers/report.go b/internal/web_server/handlers/report.go index 05c7115..cdd5153 100644 --- a/internal/web_server/handlers/report.go +++ b/internal/web_server/handlers/report.go @@ -52,7 +52,14 @@ func (h *Handler) GetDashboardReport(c *fiber.Ctx) error { }) } - return c.Status(fiber.StatusOK).JSON(summary) + return c.Status(fiber.StatusOK).JSON(domain.Response{ + Message: "Dashboard reports generated successfully", + Success: true, + StatusCode: 200, + Data: summary, + }) + + // return c.Status(fiber.StatusOK).JSON(summary) } // parseReportFilter parses query parameters into ReportFilter diff --git a/internal/web_server/routes.go b/internal/web_server/routes.go index 46cb6f0..57d2184 100644 --- a/internal/web_server/routes.go +++ b/internal/web_server/routes.go @@ -7,7 +7,7 @@ import ( _ "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/logger/mongoLogger" // "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet/monitor" @@ -230,7 +230,7 @@ func (a *App) initAppRoutes() { //mongoDB logs ctx := context.Background() - group.Get("/logs", mongoLogger.GetLogsHandler(ctx)) + group.Get("/logs", handlers.GetLogsHandler(ctx)) // Recommendation Routes group.Get("/virtual-games/recommendations/:userID", h.GetRecommendations)