diff --git a/db/query/bet.sql b/db/query/bet.sql index 61a3d02..7d3eab5 100644 --- a/db/query/bet.sql +++ b/db/query/bet.sql @@ -58,6 +58,10 @@ WHERE cashout_id = $1; SELECT * FROM bet_with_outcomes WHERE branch_id = $1; +-- name: GetBetByUserID :many +SELECT * +FROM bet_with_outcomes +WHERE user_id = $1; -- name: GetBetOutcomeByEventID :many SELECT * FROM bet_outcomes diff --git a/gen/db/bet.sql.go b/gen/db/bet.sql.go index 823fb43..3857c40 100644 --- a/gen/db/bet.sql.go +++ b/gen/db/bet.sql.go @@ -243,6 +243,47 @@ func (q *Queries) GetBetByID(ctx context.Context, id int64) (BetWithOutcome, err return i, err } +const GetBetByUserID = `-- name: GetBetByUserID :many +SELECT id, amount, total_odds, status, full_name, phone_number, branch_id, user_id, cashed_out, cashout_id, created_at, updated_at, is_shop_bet, outcomes +FROM bet_with_outcomes +WHERE user_id = $1 +` + +func (q *Queries) GetBetByUserID(ctx context.Context, userID pgtype.Int8) ([]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.Amount, + &i.TotalOdds, + &i.Status, + &i.FullName, + &i.PhoneNumber, + &i.BranchID, + &i.UserID, + &i.CashedOut, + &i.CashoutID, + &i.CreatedAt, + &i.UpdatedAt, + &i.IsShopBet, + &i.Outcomes, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + 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 diff --git a/internal/domain/bet.go b/internal/domain/bet.go index d681bb8..9c5b53b 100644 --- a/internal/domain/bet.go +++ b/internal/domain/bet.go @@ -52,6 +52,7 @@ type Bet struct { IsShopBet bool CashedOut bool CashoutID string + CreatedAt time.Time } type GetBet struct { @@ -67,6 +68,7 @@ type GetBet struct { CashedOut bool CashoutID string Outcomes []BetOutcome + CreatedAt time.Time } type CreateBet struct { @@ -127,6 +129,7 @@ type BetRes struct { IsShopBet bool `json:"is_shop_bet" example:"false"` CashedOut bool `json:"cashed_out" example:"false"` CashedID string `json:"cashed_id" example:"21234"` + CreatedAt time.Time `json:"created_at" example:"2025-04-08T12:00:00Z"` } func ConvertCreateBet(bet Bet, createdNumber int64) CreateBetRes { @@ -158,5 +161,6 @@ func ConvertBet(bet GetBet) BetRes { IsShopBet: bet.IsShopBet, CashedOut: bet.CashedOut, CashedID: bet.CashoutID, + CreatedAt: bet.CreatedAt, } } diff --git a/internal/repository/bet.go b/internal/repository/bet.go index 81a501c..24fe5b8 100644 --- a/internal/repository/bet.go +++ b/internal/repository/bet.go @@ -29,6 +29,7 @@ func convertDBBet(bet dbgen.Bet) domain.Bet { IsShopBet: bet.IsShopBet, CashedOut: bet.CashedOut, CashoutID: bet.CashoutID, + CreatedAt: bet.CreatedAt.Time, } } @@ -78,6 +79,7 @@ func convertDBBetWithOutcomes(bet dbgen.BetWithOutcome) domain.GetBet { CashedOut: bet.CashedOut, CashoutID: bet.CashoutID, Outcomes: outcomes, + CreatedAt: bet.CreatedAt.Time, } } @@ -198,6 +200,24 @@ func (s *Store) GetBetByBranchID(ctx context.Context, BranchID int64) ([]domain. return result, nil } +func (s *Store) GetBetByUserID(ctx context.Context, UserID int64) ([]domain.GetBet, error) { + bets, err := s.queries.GetBetByUserID(ctx, pgtype.Int8{ + Int64: UserID, + Valid: true, + }) + + if err != nil { + return nil, err + } + + var result []domain.GetBet = make([]domain.GetBet, 0, len(bets)) + for _, bet := range bets { + result = append(result, convertDBBetWithOutcomes(bet)) + } + + return result, nil +} + func (s *Store) UpdateCashOut(ctx context.Context, id int64, cashedOut bool) error { err := s.queries.UpdateCashOut(ctx, dbgen.UpdateCashOutParams{ ID: id, diff --git a/internal/services/bet/port.go b/internal/services/bet/port.go index cdd1ea0..452083a 100644 --- a/internal/services/bet/port.go +++ b/internal/services/bet/port.go @@ -13,6 +13,7 @@ type BetStore interface { GetBetByID(ctx context.Context, id int64) (domain.GetBet, error) GetAllBets(ctx context.Context) ([]domain.GetBet, error) GetBetByBranchID(ctx context.Context, BranchID int64) ([]domain.GetBet, error) + GetBetByUserID(ctx context.Context, UserID int64) ([]domain.GetBet, error) GetBetOutcomeByEventID(ctx context.Context, eventID int64) ([]domain.BetOutcome, error) GetBetOutcomeByBetID(ctx context.Context, betID int64) ([]domain.BetOutcome, error) UpdateCashOut(ctx context.Context, id int64, cashedOut bool) error diff --git a/internal/services/bet/service.go b/internal/services/bet/service.go index 4e3f9bf..65af3d7 100644 --- a/internal/services/bet/service.go +++ b/internal/services/bet/service.go @@ -248,6 +248,12 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID return domain.CreateBetRes{}, err } + newBet.UserID = domain.ValidInt64{ + Value: userID, + Valid: true, + } + newBet.IsShopBet = false + default: return domain.CreateBetRes{}, fmt.Errorf("Unknown Role Type") } @@ -485,6 +491,10 @@ func (s *Service) GetBetByBranchID(ctx context.Context, branchID int64) ([]domai return s.betStore.GetBetByBranchID(ctx, branchID) } +func (s *Service) GetBetByUserID(ctx context.Context, UserID int64) ([]domain.GetBet, error) { + return s.betStore.GetBetByUserID(ctx, UserID) +} + func (s *Service) UpdateCashOut(ctx context.Context, id int64, cashedOut bool) error { return s.betStore.UpdateCashOut(ctx, id, cashedOut) } diff --git a/internal/services/event/service.go b/internal/services/event/service.go index fe51aa7..dca4de6 100644 --- a/internal/services/event/service.go +++ b/internal/services/event/service.go @@ -104,7 +104,7 @@ func (s *service) FetchUpcomingEvents(ctx context.Context) error { for _, sportID := range sportIDs { var totalPages int = 1 var page int = 0 - var limit int = 10 + var limit int = 150 var count int = 0 for page <= totalPages { page = page + 1 diff --git a/internal/services/wallet/wallet.go b/internal/services/wallet/wallet.go index feb29d0..cf9cd4c 100644 --- a/internal/services/wallet/wallet.go +++ b/internal/services/wallet/wallet.go @@ -87,7 +87,7 @@ func (s *Service) DeductFromWallet(ctx context.Context, id int64, amount domain. return ErrBalanceInsufficient } - return s.walletStore.UpdateBalance(ctx, id, wallet.Balance+amount) + return s.walletStore.UpdateBalance(ctx, id, wallet.Balance-amount) } func (s *Service) UpdateWalletActive(ctx context.Context, id int64, isActive bool) error { diff --git a/internal/web_server/cron.go b/internal/web_server/cron.go index 8e0bfdd..e07a014 100644 --- a/internal/web_server/cron.go +++ b/internal/web_server/cron.go @@ -52,7 +52,7 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S } for _, job := range schedule { - job.task() + // job.task() if _, err := c.AddFunc(job.spec, job.task); err != nil { log.Fatalf("Failed to schedule cron job: %v", err) } diff --git a/internal/web_server/handlers/bet_handler.go b/internal/web_server/handlers/bet_handler.go index b01fbd3..b5f87ec 100644 --- a/internal/web_server/handlers/bet_handler.go +++ b/internal/web_server/handlers/bet_handler.go @@ -45,7 +45,7 @@ func (h *Handler) CreateBet(c *fiber.Ctx) error { h.logger.Error("PlaceBet failed", "error", err) switch err { case bet.ErrEventHasBeenRemoved, bet.ErrEventHasNotEnded, bet.ErrRawOddInvalid, wallet.ErrBalanceInsufficient: - return fiber.NewError(fiber.StatusBadGateway, err.Error()) + return fiber.NewError(fiber.StatusBadRequest, err.Error()) } return fiber.NewError(fiber.StatusInternalServerError, "Unable to create bet") } diff --git a/internal/web_server/handlers/transaction_handler.go b/internal/web_server/handlers/transaction_handler.go index b9c5d6a..8918023 100644 --- a/internal/web_server/handlers/transaction_handler.go +++ b/internal/web_server/handlers/transaction_handler.go @@ -172,7 +172,10 @@ func (h *Handler) CreateTransaction(c *fiber.Ctx) error { } user, err := h.userSvc.GetUserByID(c.Context(), userID) - + if err != nil { + h.logger.Error("CreateTransactionReq failed, user id invalid", "error", err) + return response.WriteJSON(c, fiber.StatusBadRequest, "User ID invalid", err, nil) + } transaction, err := h.transactionSvc.CreateTransaction(c.Context(), domain.CreateTransaction{ BranchID: branchID, CashierID: userID, diff --git a/internal/web_server/handlers/user.go b/internal/web_server/handlers/user.go index a0121f9..aabea39 100644 --- a/internal/web_server/handlers/user.go +++ b/internal/web_server/handlers/user.go @@ -591,3 +591,34 @@ func (h *Handler) UpdateUserSuspend(c *fiber.Ctx) error { } return response.WriteJSON(c, fiber.StatusOK, "User suspend status updated successfully", res, nil) } + +// GetBetByUserID godoc +// @Summary Gets user bets +// @Description Gets user bets +// @Tags user +// @Accept json +// @Produce json +// @Success 200 {array} domain.BetRes +// @Failure 400 {object} response.APIResponse +// @Failure 500 {object} response.APIResponse +// @Router /user/bets [get] +func (h *Handler) GetBetByUserID(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.StatusInternalServerError, "Invalid user identification") + } + + bets, err := h.betSvc.GetBetByUserID(c.Context(), userID) + if err != nil { + h.logger.Error("Failed to get bets", "error", err) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve bets") + } + + 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) +} diff --git a/internal/web_server/routes.go b/internal/web_server/routes.go index 51a9d1c..9854342 100644 --- a/internal/web_server/routes.go +++ b/internal/web_server/routes.go @@ -79,6 +79,7 @@ func (a *App) initAppRoutes() { a.fiber.Get("/user/single/:id", a.authMiddleware, h.GetUserByID) a.fiber.Delete("/user/delete/:id", a.authMiddleware, h.DeleteUser) a.fiber.Post("/user/suspend", a.authMiddleware, h.UpdateUserSuspend) + a.fiber.Get("/user/bets", a.authMiddleware, h.GetBetByUserID) a.fiber.Get("/user/wallet", a.authMiddleware, h.GetCustomerWallet) a.fiber.Post("/user/search", a.authMiddleware, h.SearchUserByNameOrPhone)