250 lines
8.2 KiB
Go
250 lines
8.2 KiB
Go
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log/slog"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket"
|
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
|
|
customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator"
|
|
"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"`
|
|
TotalOdds float32 `json:"total_odds" example:"4.22"`
|
|
}
|
|
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 CreateTicket(logger *slog.Logger, ticketSvc *ticket.Service, eventSvc event.Service, oddSvc odds.ServiceImpl, validator *customvalidator.CustomValidator) fiber.Handler {
|
|
return func(c *fiber.Ctx) error {
|
|
var req CreateTicketReq
|
|
if err := c.BodyParser(&req); err != nil {
|
|
logger.Error("CreateTicketReq failed", "error", err)
|
|
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil)
|
|
}
|
|
|
|
valErrs, ok := validator.Validate(c, req)
|
|
if !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))
|
|
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 := 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 := oddSvc.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 {
|
|
fmt.Println("Failed to unmarshal raw odd:", 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)
|
|
|
|
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 := ticketSvc.CreateTicket(c.Context(), domain.CreateTicket{
|
|
Amount: domain.ToCurrency(req.Amount),
|
|
TotalOdds: req.TotalOdds,
|
|
})
|
|
if err != nil {
|
|
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 := ticketSvc.CreateTicketOutcome(c.Context(), outcomes)
|
|
|
|
if err != nil {
|
|
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 GetTicketByID(logger *slog.Logger, ticketSvc *ticket.Service,
|
|
validator *customvalidator.CustomValidator) fiber.Handler {
|
|
return func(c *fiber.Ctx) error {
|
|
ticketID := c.Params("id")
|
|
|
|
id, err := strconv.ParseInt(ticketID, 10, 64)
|
|
if err != nil {
|
|
logger.Error("Invalid ticket ID", "ticketID", ticketID, "error", err)
|
|
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid ticket ID", err, nil)
|
|
}
|
|
|
|
ticket, err := ticketSvc.GetTicketByID(c.Context(), id)
|
|
|
|
if err != nil {
|
|
logger.Error("Failed to get ticket by ID", "ticketID", id, "error", err)
|
|
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve ticket", err, nil)
|
|
}
|
|
|
|
res := TicketRes{
|
|
ID: ticket.ID,
|
|
Outcomes: ticket.Outcomes,
|
|
Amount: ticket.Amount.Float64(),
|
|
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 GetAllTickets(logger *slog.Logger, ticketSvc *ticket.Service,
|
|
validator *customvalidator.CustomValidator) fiber.Handler {
|
|
return func(c *fiber.Ctx) error {
|
|
tickets, err := ticketSvc.GetAllTickets(c.Context())
|
|
|
|
if err != nil {
|
|
logger.Error("Failed to get tickets", "error", err)
|
|
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve tickets", err, nil)
|
|
}
|
|
|
|
var res []TicketRes = make([]TicketRes, 0, len(tickets))
|
|
|
|
for _, ticket := range tickets {
|
|
res = append(res, TicketRes{
|
|
ID: ticket.ID,
|
|
Outcomes: ticket.Outcomes,
|
|
Amount: ticket.Amount.Float64(),
|
|
TotalOdds: ticket.TotalOdds,
|
|
})
|
|
}
|
|
|
|
return response.WriteJSON(c, fiber.StatusOK, "All Tickets retrieved", res, nil)
|
|
|
|
}
|
|
}
|