Yimaru-BackEnd/internal/web_server/handlers/bet_handler.go

454 lines
14 KiB
Go

package handlers
import (
"strconv"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
"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 /sport/bet [post]
func (h *Handler) CreateBet(c *fiber.Ctx) error {
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")
}
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 response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
res, err := h.betSvc.PlaceBet(c.Context(), req, userID, role)
if err != nil {
h.mongoLoggerSvc.Error("PlaceBet failed",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
switch err {
case bet.ErrEventHasBeenRemoved, bet.ErrEventHasNotEnded, bet.ErrRawOddInvalid, wallet.ErrBalanceInsufficient:
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
return fiber.NewError(fiber.StatusInternalServerError, "Unable to create bet")
}
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)
}
// 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 /sport/random/bet [post]
func (h *Handler) RandomBet(c *fiber.Ctx) error {
userID := c.Locals("user_id").(int64)
leagueIDQuery, err := strconv.Atoi(c.Query("league_id"))
if err != nil {
h.mongoLoggerSvc.Error("invalid league id",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return response.WriteJSON(c, fiber.StatusBadRequest, "invalid league id", nil, nil)
}
sportIDQuery, err := strconv.Atoi(c.Query("sport_id"))
if err != nil {
h.mongoLoggerSvc.Error("invalid sport id",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
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.ValidInt32{
Value: int32(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.Error("invalid start_time format",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid start_time format", nil, nil)
}
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.Error("invalid start_time format",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid start_time format", nil, nil)
}
lastStartTime = domain.ValidTime{
Value: lastStartTimeParsed,
Valid: true,
}
}
var req domain.RandomBetReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Error("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")
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
h.mongoLoggerSvc.Error("RandomBet validation failed",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Any("validation_errors", valErrs),
zap.Time("timestamp", time.Now()),
)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
var res domain.CreateBetRes
for i := 0; i < int(req.NumberOfBets); i++ {
res, err = h.betSvc.PlaceRandomBet(c.Context(), userID, req.BranchID, leagueID, sportID, firstStartTime, lastStartTime)
if err != nil {
h.mongoLoggerSvc.Error("Random Bet failed",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
switch err {
case bet.ErrNoEventsAvailable:
return fiber.NewError(fiber.StatusBadRequest, "No events found")
}
return fiber.NewError(fiber.StatusInternalServerError, "Unable to create random bet")
}
}
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 /sport/bet [get]
func (h *Handler) GetAllBet(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
branchID := c.Locals("branch_id").(domain.ValidInt64)
var isShopBet domain.ValidBool
isShopBetQuery := c.Query("is_shop")
if isShopBetQuery != "" {
isShopBetParse, err := strconv.ParseBool(isShopBetQuery)
if err != nil {
h.mongoLoggerSvc.Error("Failed to parse is_shop_bet",
zap.Int("status_code", fiber.StatusBadRequest),
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,
}
}
bets, err := h.betSvc.GetAllBets(c.Context(), domain.BetFilter{
BranchID: branchID,
CompanyID: companyID,
IsShopBet: isShopBet,
})
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, "Failed to retrieve bets")
}
res := make([]domain.BetRes, len(bets))
for i, bet := range bets {
res[i] = domain.ConvertBet(bet)
}
h.mongoLoggerSvc.Info("All bets retrieved successfully",
zap.Int("status_code", fiber.StatusOK),
zap.Time("timestamp", time.Now()),
)
return response.WriteJSON(c, fiber.StatusOK, "All bets retrieved successfully", res, nil)
}
// 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 /sport/bet/{id} [get]
func (h *Handler) GetBetByID(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")
}
bet, err := h.betSvc.GetBetByID(c.Context(), id)
if err != nil {
h.mongoLoggerSvc.Error("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)
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)
}
// GetBetByCashoutID godoc
// @Summary Gets bet by cashout id
// @Description Gets a single bet by cashout id
// @Tags bet
// @Accept json
// @Produce json
// @Param id path string true "cashout ID"
// @Success 200 {object} domain.BetRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /sport/bet/cashout/{id} [get]
func (h *Handler) GetBetByCashoutID(c *fiber.Ctx) error {
cashoutID := c.Params("id")
bet, err := h.betSvc.GetBetByCashoutID(c.Context(), cashoutID)
if err != nil {
h.mongoLoggerSvc.Error("Failed to get bet by cashout ID",
zap.String("cashoutID", cashoutID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve bet", err, nil)
}
res := domain.ConvertBet(bet)
h.mongoLoggerSvc.Info("Bet retrieved successfully by cashout ID",
zap.String("cashoutID", cashoutID),
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 /sport/bet/{id} [patch]
func (h *Handler) UpdateCashOut(c *fiber.Ctx) error {
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 response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request body", err, nil)
}
if valErrs, ok := h.validator.Validate(c, req); !ok {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
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 /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.DeleteBet(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")
}
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)
}