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

684 lines
22 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"
)
// CreateShopBet godoc
// @Summary Create bet at branch
// @Description Create bet at branch
// @Tags transaction
// @Accept json
// @Produce json
// @Param createBet body domain.ShopBetReq true "create bet"
// @Success 200 {object} domain.ShopTransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/shop/bet [post]
func (h *Handler) CreateShopBet(c *fiber.Ctx) error {
userID := c.Locals("user_id").(int64)
role := c.Locals("role").(domain.Role)
company_id := c.Locals("company_id").(domain.ValidInt64)
var req domain.ShopBetReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("CreateBetReq failed to parse request",
zap.Any("request", req),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "invalid request body"+err.Error())
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
shopBet, err := h.transactionSvc.CreateShopBet(c.Context(), userID, role, company_id, req)
if err != nil {
var statusCode int
if isBetError := h.betSvc.CheckIfBetError(err); isBetError {
statusCode = fiber.StatusBadRequest
} else {
statusCode = fiber.StatusInternalServerError
}
h.mongoLoggerSvc.Info("Failed to create shop bet",
zap.Int64("userID", userID),
zap.String("role", string(role)),
zap.Int("status_code", statusCode),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(statusCode, "failed to create shop bet"+err.Error())
}
res := domain.ConvertShopBet(shopBet)
return response.WriteJSON(c, fiber.StatusOK, "Transaction created successfully", res, nil)
}
// CashoutBet godoc
// @Summary Cashout bet at branch
// @Description Cashout bet at branch
// @Tags transaction
// @Accept json
// @Produce json
// @Param createBet body domain.CashoutReq true "cashout bet"
// @Success 200 {object} domain.ShopTransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/shop/bet/{id} [get]
func (h *Handler) GetShopBetByBetID(c *fiber.Ctx) error {
betIDstr := c.Params("id")
betID, err := strconv.ParseInt(betIDstr, 10, 64)
if err != nil {
h.mongoLoggerSvc.Info("GetShopBetByBetID failed bet id is invalid",
zap.String("betID", betIDstr),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "bet ID is invalid:"+err.Error())
}
bet, err := h.transactionSvc.GetShopBetByBetID(c.Context(), betID)
if err != nil {
h.mongoLoggerSvc.Error("GetShopBetByBetID failed invalid bet id",
zap.Int64("betID", betID),
zap.Int("status_code", fiber.StatusNotFound),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusNotFound, err.Error())
}
res := domain.ConvertShopBetDetail(bet)
return response.WriteJSON(c, fiber.StatusOK, "Shop bet fetched successfully", res, nil)
}
// GetAllShopBets godoc
// @Summary Gets all shop bets
// @Description Gets all the shop bets
// @Tags bet
// @Accept json
// @Produce json
// @Success 200 {array} domain.ShopBetRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/shop/bet [get]
func (h *Handler) GetAllShopBets(c *fiber.Ctx) error {
// role := c.Locals("role").(domain.Role)
companyID := c.Locals("company_id").(domain.ValidInt64)
branchID := c.Locals("branch_id").(domain.ValidInt64)
searchQuery := c.Query("query")
searchString := domain.ValidString{
Value: searchQuery,
Valid: searchQuery != "",
}
createdBeforeQuery := c.Query("created_before")
var createdBefore domain.ValidTime
if createdBeforeQuery != "" {
createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery)
if err != nil {
h.mongoLoggerSvc.Info("invalid created_before format",
zap.String("time", createdBeforeQuery),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid created_before format")
}
createdBefore = domain.ValidTime{
Value: createdBeforeParsed,
Valid: true,
}
}
createdAfterQuery := c.Query("created_after")
var createdAfter domain.ValidTime
if createdAfterQuery != "" {
createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery)
if err != nil {
h.mongoLoggerSvc.Info("invalid created_after format",
zap.String("created_after", createdAfterQuery),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid created_after format")
}
createdAfter = domain.ValidTime{
Value: createdAfterParsed,
Valid: true,
}
}
bets, err := h.transactionSvc.GetAllShopBet(c.Context(), domain.ShopBetFilter{
Query: searchString,
CreatedBefore: createdBefore,
CreatedAfter: createdAfter,
CompanyID: companyID,
BranchID: branchID,
})
if err != nil {
h.mongoLoggerSvc.Error("Failed to get all bets",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve bets፡"+err.Error())
}
res := make([]domain.ShopBetRes, len(bets))
for i, bet := range bets {
res[i] = domain.ConvertShopBetDetail(bet)
}
return response.WriteJSON(c, fiber.StatusOK, "All bets retrieved successfully", res, nil)
}
// CashoutBet godoc
// @Summary Cashout bet at branch
// @Description Cashout bet at branch
// @Tags transaction
// @Accept json
// @Produce json
// @Param cashoutBet body domain.CashoutReq true "cashout bet"
// @Success 200 {object} domain.ShopTransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/shop/bet/{id}/cashout [post]
func (h *Handler) CashoutBet(c *fiber.Ctx) error {
betIDStr := c.Params("id")
betID, err := strconv.ParseInt(betIDStr, 10, 64)
if err != nil {
h.mongoLoggerSvc.Info("CashoutReq invalid bet id",
zap.String("betID", betIDStr),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid bet id")
}
userID := c.Locals("user_id").(int64)
role := c.Locals("role").(domain.Role)
companyID := c.Locals("company_id").(domain.ValidInt64)
var req domain.CashoutReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("CashoutReq failed to parse request",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.mongoLoggerSvc.Info("CashoutReq validation failed",
zap.Any("request", req),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
transaction, err := h.transactionSvc.CashoutBet(c.Context(), betID, userID, role, req, companyID)
if err != nil {
h.mongoLoggerSvc.Error("Failed to cashout bet",
zap.Int64("userID", userID),
zap.Int64("betID", betID),
zap.String("role", string(role)),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
res := domain.ConvertShopTransaction(transaction)
return response.WriteJSON(c, fiber.StatusOK, "Transaction created successfully", res, nil)
}
// CashoutByCashoutID godoc
// @Summary Cashout bet by cashoutID
// @Description Cashout bet by cashoutID
// @Tags transaction
// @Accept json
// @Produce json
// @Param cashoutBet body domain.CashoutReq true "cashout bet"
// @Success 200 {object} domain.ShopTransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/shop/cashout [post]
func (h *Handler) CashoutByCashoutID(c *fiber.Ctx) error {
userID := c.Locals("user_id").(int64)
role := c.Locals("role").(domain.Role)
companyID := c.Locals("company_id").(domain.ValidInt64)
var req domain.CashoutReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("CashoutReq failed to parse request",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid request")
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.mongoLoggerSvc.Info("CashoutReq validation failed",
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusBadRequest),
zap.String("error", errMsg),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
bet, err := h.transactionSvc.GetShopBetByCashoutID(c.Context(), req.CashoutID)
if err != nil {
h.mongoLoggerSvc.Info("CashoutReq failed invalid cashout id",
zap.String("CashoutID", req.CashoutID),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
transaction, err := h.transactionSvc.CashoutBet(c.Context(), bet.BetID, userID, role, req, companyID)
if err != nil {
h.mongoLoggerSvc.Info("Failed to cashout bet",
zap.Int64("BetID", bet.BetID),
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
res := domain.ConvertShopTransaction(transaction)
return response.WriteJSON(c, fiber.StatusOK, "Transaction created successfully", res, nil)
}
// CashoutBet godoc
// @Summary Cashout bet at branch
// @Description Cashout bet at branch
// @Tags transaction
// @Accept json
// @Produce json
// @Param createBet body domain.CashoutReq true "cashout bet"
// @Success 200 {object} domain.ShopTransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/shop/cashout/{id} [get]
func (h *Handler) GetShopBetByCashoutID(c *fiber.Ctx) error {
cashoutID := c.Params("id")
if cashoutID == "" {
h.mongoLoggerSvc.Info("CashoutReq failed cashout id is required",
zap.String("cashoutID", cashoutID),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "cashout ID is required")
}
bet, err := h.transactionSvc.GetShopBetByCashoutID(c.Context(), cashoutID)
if err != nil {
h.mongoLoggerSvc.Info("CashoutReq failed invalid cashout id",
zap.String("CashoutID", cashoutID),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
res := domain.ConvertShopBetDetail(bet)
return response.WriteJSON(c, fiber.StatusOK, "Transaction created successfully", res, nil)
}
// DepositForCustomer godoc
// @Summary Shop deposit into customer wallet
// @Description Transfers money from branch wallet to customer wallet
// @Tags transaction
// @Accept json
// @Produce json
// @Param transferToWallet body domain.ShopDepositReq true "ShopDepositReq"
// @Success 200 {object} domain.ShopDepositRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/shop/deposit [post]
func (h *Handler) DepositForCustomer(c *fiber.Ctx) error {
// Get sender ID from the cashier
userID := c.Locals("user_id").(int64)
role := c.Locals("role").(domain.Role)
var req domain.ShopDepositReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("CreateTransferReq failed",
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.mongoLoggerSvc.Info("Failed to validate customer deposit",
zap.Any("request", req),
zap.Int("status_code", fiber.StatusBadRequest),
zap.String("error", errMsg),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
deposit, err := h.transactionSvc.CreateShopDeposit(c.Context(), userID, role, req)
if err != nil {
h.mongoLoggerSvc.Info("failed to create shop deposit",
zap.Int64("userID", userID),
zap.String("role", string(role)),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
res := domain.ConvertShopDeposit(deposit)
return response.WriteJSON(c, fiber.StatusOK, "Transfer Successful", res, nil)
}
// GetAllTransactions godoc
// @Summary Gets all transactions
// @Description Gets all the transactions
// @Tags transaction
// @Accept json
// @Produce json
// @Success 200 {array} domain.ShopTransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/shop/transaction [get]
func (h *Handler) GetAllTransactions(c *fiber.Ctx) error {
// Get user_id from middleware
// userID := c.Locals("user_id").(int64)
// role := c.Locals("role").(domain.Role)
companyID := c.Locals("company_id").(domain.ValidInt64)
branchID := c.Locals("branch_id").(domain.ValidInt64)
searchQuery := c.Query("query")
searchString := domain.ValidString{
Value: searchQuery,
Valid: searchQuery != "",
}
createdBeforeQuery := c.Query("created_before")
var createdBefore domain.ValidTime
if createdBeforeQuery != "" {
createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery)
if err != nil {
h.mongoLoggerSvc.Info("invalid start_time format",
zap.String("createdBefore", createdBeforeQuery),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid created_before format")
}
createdBefore = domain.ValidTime{
Value: createdBeforeParsed,
Valid: true,
}
}
createdAfterQuery := c.Query("created_after")
var createdAfter domain.ValidTime
if createdAfterQuery != "" {
createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery)
if err != nil {
h.mongoLoggerSvc.Info("invalid start_time format",
zap.String("createdAfter", createdAfterQuery),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid created_after format")
}
createdAfter = domain.ValidTime{
Value: createdAfterParsed,
Valid: true,
}
}
// Check user role and fetch transactions accordingly
transactions, err := h.transactionSvc.GetAllShopTransactions(c.Context(), domain.ShopTransactionFilter{
CompanyID: companyID,
BranchID: branchID,
Query: searchString,
CreatedBefore: createdBefore,
CreatedAfter: createdAfter,
})
if err != nil {
h.mongoLoggerSvc.Info("Failed to get transactions",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
res := make([]domain.ShopTransactionRes, len(transactions))
for i, transaction := range transactions {
res[i] = domain.ConvertShopTransactionDetail(transaction)
}
return response.WriteJSON(c, fiber.StatusOK, "Transactions retrieved successfully", res, nil)
}
// GetTransactionByID godoc
// @Summary Gets transaction by id
// @Description Gets a single transaction by id
// @Tags transaction
// @Accept json
// @Produce json
// @Param id path int true "Transaction ID"
// @Success 200 {object} domain.ShopTransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/shop/transaction/{id} [get]
func (h *Handler) GetTransactionByID(c *fiber.Ctx) error {
transactionID := c.Params("id")
id, err := strconv.ParseInt(transactionID, 10, 64)
if err != nil {
h.mongoLoggerSvc.Info("Invalid transaction ID",
zap.String("transactionID", transactionID),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid transaction ID")
}
transaction, err := h.transactionSvc.GetShopTransactionByID(c.Context(), id)
if err != nil {
h.mongoLoggerSvc.Error("Failed to get shop transaction by ID",
zap.Int64("transactionID", id),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve transaction")
}
res := domain.ConvertShopTransactionDetail(transaction)
return response.WriteJSON(c, fiber.StatusOK, "Transaction retrieved successfully", res, nil)
}
// GetShopBetByTransactionID godoc
// @Summary Gets shop bet by transaction id
// @Description Gets a single shop bet by transaction id
// @Tags transaction
// @Accept json
// @Produce json
// @Param id path int true "Transaction ID"
// @Success 200 {object} domain.ShopTransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/shop/transaction/{id}/bet [get]
func (h *Handler) GetShopBetByTransactionID(c *fiber.Ctx) error {
transactionID := c.Params("id")
id, err := strconv.ParseInt(transactionID, 10, 64)
if err != nil {
h.mongoLoggerSvc.Info("Invalid transaction ID",
zap.String("transactionID", transactionID),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
transaction, err := h.transactionSvc.GetShopBetByShopTransactionID(c.Context(), id)
if err != nil {
h.mongoLoggerSvc.Error("Failed to get transaction by ID",
zap.Int64("transactionID", id),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
res := domain.ConvertShopBetDetail(transaction)
return response.WriteJSON(c, fiber.StatusOK, "Shop bet retrieved successfully", res, nil)
}
// UpdateTransactionVerified godoc
// @Summary Updates the verified field of a transaction
// @Description Updates the verified status of a transaction
// @Tags transaction
// @Accept json
// @Produce json
// @Param id path int true "Transaction ID"
// @Param updateVerified body domain.UpdateTransactionVerifiedReq true "Updates Transaction Verification"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/shop/transaction/{id} [put]
func (h *Handler) UpdateTransactionVerified(c *fiber.Ctx) error {
transactionID := c.Params("id")
userID := c.Locals("user_id").(int64)
companyID := c.Locals("company_id").(domain.ValidInt64)
branchID := c.Locals("branch_id").(domain.ValidInt64)
role := c.Locals("role").(domain.Role)
id, err := strconv.ParseInt(transactionID, 10, 64)
if err != nil {
h.mongoLoggerSvc.Info("Invalid transaction ID",
zap.String("transactionID", transactionID),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid transaction ID")
}
var req domain.UpdateTransactionVerifiedReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse UpdateTransactionVerified request",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid request")
}
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 UpdateTransactionVerified",
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
zap.String("errMsg", errMsg),
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
err = h.transactionSvc.UpdateShopTransactionVerified(c.Context(), id, req.Verified, userID, role, companyID, branchID)
if err != nil {
h.mongoLoggerSvc.Error("Failed to update transaction verification",
zap.Int64("transactionID", id),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update transaction verification")
}
return response.WriteJSON(c, fiber.StatusOK, "Transaction updated successfully", nil, nil)
}