Yimaru-BackEnd/internal/web_server/handlers/ticket_handler.go
Samuel Tariku 3dfa1255b0 Refactor result notification service and remove redundant code
- Removed the CheckAndSendResultNotifications method from the result service.
- Consolidated notification logic into a new notification.go file.
- Updated email and in-app notification formatting to include event processing periods.
- Added error handling for wallet operations to check if wallets are active before processing transfers.
- Introduced new error for disabled wallets.
- Updated cron jobs to comment out unnecessary tasks.
- Added bulk update functionality for bet outcomes by odd IDs in the odd handler.
- Renamed ticket handler methods for clarity and consistency.
- Updated API version in routes.
2025-10-10 14:59:19 +03:00

270 lines
8.1 KiB
Go

package handlers
import (
"fmt"
"strconv"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
)
// CreateTicket godoc
// @Summary Create a temporary ticket
// @Description Creates a temporary ticket
// @Tags ticket
// @Accept json
// @Produce json
// @Param createTicket body domain.CreateTicketReq true "Creates ticket"
// @Success 200 {object} domain.CreateTicketRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/ticket [post]
func (h *Handler) CreateTenantTicket(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req domain.CreateTicketReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse CreateTicket request",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
}
if valErrs, ok := h.validator.Validate(c, req); !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.mongoLoggerSvc.Info("Failed to validate CreateTicketReq",
zap.Any("CreateTicketReq", req),
zap.Int("status_code", fiber.StatusBadRequest),
zap.String("error", errMsg),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
newTicket, rows, err := h.ticketSvc.CreateTicket(c.Context(), req, c.IP(), companyID.Value)
if err != nil {
var statusCode int
if isTicketError := h.ticketSvc.CheckTicketError(err); isTicketError {
statusCode = fiber.StatusBadRequest
} else {
statusCode = fiber.StatusInternalServerError
}
h.mongoLoggerSvc.Error("Failed to create ticket",
zap.Any("req", req),
zap.Int("status_code", statusCode),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(statusCode, err.Error())
}
res := domain.CreateTicketRes{
FastCode: newTicket.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} domain.TicketRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/ticket/{id} [get]
func (h *Handler) GetTenantTicketByID(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
ticketID := c.Params("id")
id, err := strconv.ParseInt(ticketID, 10, 64)
if err != nil {
h.mongoLoggerSvc.Info("Invalid ticket ID",
zap.String("ticketID", ticketID),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid ticket ID")
}
ticket, err := h.ticketSvc.GetTicketByID(c.Context(), id)
if err != nil {
h.mongoLoggerSvc.Info("Failed to get ticket by ID",
zap.Int64("ticketID", id),
zap.Int("status_code", fiber.StatusNotFound),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve ticket")
}
if ticket.CompanyID != companyID.Value {
h.mongoLoggerSvc.Warn("User attempt to access another company ticket",
zap.Int64("ticketID", id),
zap.Int64("ticket CompanyID", ticket.CompanyID),
zap.Int64("companyID", companyID.Value),
zap.Bool("companyID Valid", companyID.Valid),
zap.Int("status_code", fiber.StatusNotFound),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve ticket")
}
res := domain.TicketRes{
ID: ticket.ID,
Outcomes: ticket.Outcomes,
Amount: ticket.Amount.Float32(),
TotalOdds: ticket.TotalOdds,
CompanyID: ticket.CompanyID,
}
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} domain.TicketRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/ticket [get]
func (h *Handler) GetAllTenantTickets(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
tickets, err := h.ticketSvc.GetAllTickets(c.Context(), domain.TicketFilter{
CompanyID: companyID,
})
if err != nil {
h.mongoLoggerSvc.Error("Failed to get tickets",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve tickets")
}
res := make([]domain.TicketRes, len(tickets))
for i, ticket := range tickets {
res[i] = domain.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)
}
// 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} domain.TicketRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/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.mongoLoggerSvc.Info("Invalid ticket ID",
zap.String("ticketID", ticketID),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid ticket ID")
}
ticket, err := h.ticketSvc.GetTicketByID(c.Context(), id)
if err != nil {
h.mongoLoggerSvc.Info("Failed to get ticket by ID",
zap.Int64("ticketID", id),
zap.Int("status_code", fiber.StatusNotFound),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve ticket")
}
res := domain.TicketRes{
ID: ticket.ID,
Outcomes: ticket.Outcomes,
Amount: ticket.Amount.Float32(),
TotalOdds: ticket.TotalOdds,
CompanyID: ticket.CompanyID,
}
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} domain.TicketRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/ticket [get]
func (h *Handler) GetAllTickets(c *fiber.Ctx) error {
tickets, err := h.ticketSvc.GetAllTickets(c.Context(), domain.TicketFilter{})
if err != nil {
h.mongoLoggerSvc.Error("Failed to get tickets",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve tickets")
}
res := make([]domain.TicketRes, len(tickets))
for i, ticket := range tickets {
res[i] = domain.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)
}