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

228 lines
7.3 KiB
Go

package handlers
import (
"encoding/json"
"strconv"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2"
)
type CreateTicketOutcomeReq struct {
// TicketID int64 `json:"ticket_id" example:"1"`
EventID int64 `json:"event_id" example:"1"`
OddID int64 `json:"odd_id" example:"1"`
MarketID int64 `json:"market_id" example:"1"`
// HomeTeamName string `json:"home_team_name" example:"Manchester"`
// AwayTeamName string `json:"away_team_name" example:"Liverpool"`
// MarketName string `json:"market_name" example:"Fulltime Result"`
// Odd float32 `json:"odd" example:"1.5"`
// OddName string `json:"odd_name" example:"1"`
// Expires time.Time `json:"expires" example:"2025-04-08T12:00:00Z"`
}
type CreateTicketReq struct {
Outcomes []CreateTicketOutcomeReq `json:"outcomes"`
Amount float32 `json:"amount" example:"100.0"`
}
type CreateTicketRes struct {
FastCode int64 `json:"fast_code" example:"1234"`
CreatedNumber int64 `json:"created_number" example:"3"`
}
type TicketRes struct {
ID int64 `json:"id" example:"1"`
Outcomes []domain.TicketOutcome `json:"outcomes"`
Amount float32 `json:"amount" example:"100.0"`
TotalOdds float32 `json:"total_odds" example:"4.22"`
}
// CreateTicket godoc
// @Summary Create a temporary ticket
// @Description Creates a temporary ticket
// @Tags ticket
// @Accept json
// @Produce json
// @Param createTicket body CreateTicketReq true "Creates ticket"
// @Success 200 {object} CreateTicketRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /ticket [post]
func (h *Handler) CreateTicket(c *fiber.Ctx) error {
var req CreateTicketReq
if err := c.BodyParser(&req); err != nil {
h.logger.Error("Failed to parse CreateTicket request", "error", err)
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
}
if valErrs, ok := h.validator.Validate(c, req); !ok {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
// TODO Validate Outcomes Here and make sure they didn't expire
// Validation for creating tickets
if len(req.Outcomes) > 30 {
return response.WriteJSON(c, fiber.StatusBadRequest, "Too many odds/outcomes selected", nil, nil)
}
var outcomes []domain.CreateTicketOutcome = make([]domain.CreateTicketOutcome, 0, len(req.Outcomes))
var totalOdds float32 = 1
for _, outcome := range req.Outcomes {
eventIDStr := strconv.FormatInt(outcome.EventID, 10)
marketIDStr := strconv.FormatInt(outcome.MarketID, 10)
oddIDStr := strconv.FormatInt(outcome.OddID, 10)
event, err := h.eventSvc.GetUpcomingEventByID(c.Context(), eventIDStr)
if err != nil {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid event id", err, nil)
}
// Checking to make sure the event hasn't already started
// currentTime := time.Now()
// if event.StartTime.Before(currentTime) {
// return response.WriteJSON(c, fiber.StatusBadRequest, "The event has already expired", nil, nil)
// }
odds, err := h.prematchSvc.GetRawOddsByMarketID(c.Context(), marketIDStr, eventIDStr)
if err != nil {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid market id", err, nil)
}
type rawOddType struct {
ID string
Name string
Odds string
Header string
Handicap string
}
var selectedOdd rawOddType
var isOddFound bool = false
for _, raw := range odds.RawOdds {
var rawOdd rawOddType
rawBytes, err := json.Marshal(raw)
err = json.Unmarshal(rawBytes, &rawOdd)
if err != nil {
h.logger.Error("Failed to unmarshal raw odd:", "error", err)
continue
}
if rawOdd.ID == oddIDStr {
selectedOdd = rawOdd
isOddFound = true
}
}
if !isOddFound {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid odd id", nil, nil)
}
parsedOdd, err := strconv.ParseFloat(selectedOdd.Odds, 32)
totalOdds = totalOdds * float32(parsedOdd)
outcomes = append(outcomes, domain.CreateTicketOutcome{
EventID: outcome.EventID,
OddID: outcome.OddID,
MarketID: outcome.MarketID,
HomeTeamName: event.HomeTeam,
AwayTeamName: event.AwayTeam,
MarketName: odds.MarketName,
Odd: float32(parsedOdd),
OddName: selectedOdd.Name,
OddHeader: selectedOdd.Header,
OddHandicap: selectedOdd.Handicap,
Expires: event.StartTime,
})
}
ticket, err := h.ticketSvc.CreateTicket(c.Context(), domain.CreateTicket{
Amount: domain.ToCurrency(req.Amount),
TotalOdds: totalOdds,
})
if err != nil {
h.logger.Error("CreateTicketReq failed", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "Internal server error",
})
}
// Add the ticket id now that it has fetched from the database
for index := range outcomes {
outcomes[index].TicketID = ticket.ID
}
rows, err := h.ticketSvc.CreateTicketOutcome(c.Context(), outcomes)
if err != nil {
h.logger.Error("CreateTicketReq failed to create outcomes", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "Internal server error",
})
}
res := CreateTicketRes{
FastCode: ticket.ID,
CreatedNumber: rows,
}
return response.WriteJSON(c, fiber.StatusOK, "Ticket Created", res, nil)
}
// GetTicketByID godoc
// @Summary Get ticket by ID
// @Description Retrieve ticket details by ticket ID
// @Tags ticket
// @Accept json
// @Produce json
// @Param id path int true "Ticket ID"
// @Success 200 {object} TicketRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /ticket/{id} [get]
func (h *Handler) GetTicketByID(c *fiber.Ctx) error {
ticketID := c.Params("id")
id, err := strconv.ParseInt(ticketID, 10, 64)
if err != nil {
h.logger.Error("Invalid ticket ID", "ticketID", ticketID, "error", err)
return fiber.NewError(fiber.StatusBadRequest, "Invalid ticket ID")
}
ticket, err := h.ticketSvc.GetTicketByID(c.Context(), id)
if err != nil {
// h.logger.Error("Failed to get ticket by ID", "ticketID", id, "error", err)
return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve ticket")
}
res := TicketRes{
ID: ticket.ID,
Outcomes: ticket.Outcomes,
Amount: ticket.Amount.Float32(),
TotalOdds: ticket.TotalOdds,
}
return response.WriteJSON(c, fiber.StatusOK, "Ticket retrieved successfully", res, nil)
}
// GetAllTickets godoc
// @Summary Get all tickets
// @Description Retrieve all tickets
// @Tags ticket
// @Accept json
// @Produce json
// @Success 200 {array} TicketRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /ticket [get]
func (h *Handler) GetAllTickets(c *fiber.Ctx) error {
tickets, err := h.ticketSvc.GetAllTickets(c.Context())
if err != nil {
h.logger.Error("Failed to get tickets", "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve tickets")
}
res := make([]TicketRes, len(tickets))
for i, ticket := range tickets {
res[i] = TicketRes{
ID: ticket.ID,
Outcomes: ticket.Outcomes,
Amount: ticket.Amount.Float32(),
TotalOdds: ticket.TotalOdds,
}
}
return response.WriteJSON(c, fiber.StatusOK, "All tickets retrieved successfully", res, nil)
}