customer/internal/interface/controller/http/cart/controllers.go

318 lines
10 KiB
Go
Raw Normal View History

package cart
import (
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
"google.golang.org/protobuf/types/known/timestamppb"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/broker/tariff"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/client"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/repository"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/discount"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils/transfer"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate"
2024-05-23 14:34:45 +00:00
"strings"
"sync"
"time"
)
type Deps struct {
MiddleWare *http.MiddleWare
Logger *zap.Logger
AccountRepo *repository.AccountRepository
HistoryRepo *repository.HistoryRepository
HubAdminClient *client.HubadminClient
DiscountClient *client.DiscountClient
CurrencyClient *client.CurrencyClient
Producer *tariff.Producer
}
type CartController struct {
middleWare *http.MiddleWare
logger *zap.Logger
accountRepo *repository.AccountRepository
historyRepo *repository.HistoryRepository
hubAdminClient *client.HubadminClient
discountClient *client.DiscountClient
currencyClient *client.CurrencyClient
producer *tariff.Producer
}
func NewCartController(deps Deps) *CartController {
return &CartController{
middleWare: deps.MiddleWare,
logger: deps.Logger,
accountRepo: deps.AccountRepo,
historyRepo: deps.HistoryRepo,
hubAdminClient: deps.HubAdminClient,
discountClient: deps.DiscountClient,
currencyClient: deps.CurrencyClient,
producer: deps.Producer,
}
}
2024-05-23 07:15:26 +00:00
func (receiver *CartController) Remove(ctx *fiber.Ctx) error {
userID, ok := receiver.middleWare.ExtractUserID(ctx)
if !ok || userID == "" {
return receiver.middleWare.NoAuth(ctx)
}
2024-05-23 07:15:26 +00:00
// todo проверить менять или нет
id := ctx.Query("id")
if id == "" {
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "empty item id")
}
cartItems, err := receiver.accountRepo.RemoveItemFromCart(ctx.Context(), userID, id)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
return ctx.Status(fiber.StatusOK).JSON(cartItems)
}
func (receiver *CartController) Add2cart(ctx *fiber.Ctx) error {
userID, ok := receiver.middleWare.ExtractUserID(ctx)
if !ok || userID == "" {
return receiver.middleWare.NoAuth(ctx)
}
token, ok := receiver.middleWare.ExtractToken(ctx)
if !ok || token == "" {
return receiver.middleWare.NoAuth(ctx)
}
2024-05-23 14:34:45 +00:00
hlogger := receiver.middleWare.ExtractLogger(ctx)
tariffID := ctx.Query("id")
if tariffID == "" {
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "empty item id")
}
tariff, err := receiver.hubAdminClient.GetTariff(ctx.Context(), token, tariffID)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
if tariff == nil {
return receiver.middleWare.Error(ctx, fiber.StatusNotFound, "tariff not found")
}
cartItems, err := receiver.accountRepo.AddItemToCart(ctx.Context(), userID, tariffID)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
2024-05-23 14:34:45 +00:00
hlogger.Emit(models.InfoAddToCart{
CtxUserIP: ctx.IP(),
CtxUserPort: ctx.Port(),
KeyDomain: strings.Join(ctx.Subdomains(), "/"),
KeyPath: ctx.Path(),
2024-05-23 14:34:45 +00:00
CtxUserID: userID,
CtxAccountID: cartItems.ID,
CtxTariffID: tariffID,
})
return ctx.Status(fiber.StatusOK).JSON(cartItems)
}
2024-05-23 07:15:26 +00:00
func (receiver *CartController) Pay(ctx *fiber.Ctx) error {
userID, ok := receiver.middleWare.ExtractUserID(ctx)
if !ok || userID == "" {
return receiver.middleWare.NoAuth(ctx)
}
accessToken, ok := receiver.middleWare.ExtractToken(ctx)
if !ok || accessToken == "" {
return receiver.middleWare.NoAuth(ctx)
}
2024-05-23 14:34:45 +00:00
hlogger := receiver.middleWare.ExtractLogger(ctx)
account, err := receiver.accountRepo.FindByUserID(ctx.Context(), userID)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
receiver.logger.Info("account for pay", zap.Any("acc", account))
tariffs, err := receiver.hubAdminClient.GetTariffs(ctx.Context(), accessToken, account.Cart)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
receiver.logger.Info("tariffs for pay", zap.Any("acc", tariffs))
tariffsAmount := utils.CalculateCartPurchasesAmount(tariffs)
discountResponse, err := receiver.discountClient.Apply(ctx.Context(), &discount.ApplyDiscountRequest{
UserInformation: &discount.UserInformation{
ID: account.UserID,
Type: string(account.Status),
PurchasesAmount: uint64(account.Wallet.Spent),
CartPurchasesAmount: tariffsAmount,
},
Products: transfer.TariffsToProductInformations(tariffs),
Date: timestamppb.New(time.Now()),
})
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
receiver.logger.Info("discountResponse for pay", zap.Any("acc", discount.ApplyDiscountRequest{
UserInformation: &discount.UserInformation{
ID: account.UserID,
Type: string(account.Status),
PurchasesAmount: uint64(account.Wallet.Spent),
CartPurchasesAmount: tariffsAmount,
},
Products: transfer.TariffsToProductInformations(tariffs),
Date: timestamppb.New(time.Now()),
}))
if account.Wallet.Money < int64(discountResponse.Price) {
2024-05-23 14:34:45 +00:00
hlogger.Emit(models.InfoPayCart{
CtxUserIP: ctx.IP(),
CtxUserPort: ctx.Port(),
KeyDomain: strings.Join(ctx.Subdomains(), "/"),
KeyPath: ctx.Path(),
2024-05-23 14:34:45 +00:00
CtxUserID: userID,
CtxAccountID: account.ID,
KeySuccess: false,
CtxPrice: discountResponse.Price - uint64(account.Wallet.Money),
CtxTariff: strings.Join(account.Cart, ","),
CtxDiscount: strings.Join(utils.GetAppliedDiscountsIDs(discountResponse.AppliedDiscounts), ","),
CtxRowPrice: tariffsAmount,
CtxRowData: utils.MarshalRawDetails(models.RawDetails{Tariffs: tariffs, Price: int64(discountResponse.Price)}),
})
return receiver.middleWare.Error(ctx, fiber.StatusPaymentRequired, "insufficient funds: %d", int64(discountResponse.Price)-account.Wallet.Money)
}
for _, applied := range discountResponse.AppliedDiscounts {
if applied.Condition.User != nil && *applied.Condition.User != "" {
_, err := receiver.discountClient.DeleteDiscount(ctx.Context(), &discount.GetDiscountByIDRequest{
ID: applied.ID,
})
if err != nil {
return receiver.middleWare.Error(ctx, fiber.StatusInternalServerError, "failed delete discount by id:%s", applied.ID)
}
}
}
// WithdrawAccountWalletMoney
request := models.WithdrawAccountWallet{
Money: int64(discountResponse.Price),
Account: account,
}
if validate.IsStringEmpty(request.Account.Wallet.Currency) {
request.Account.Wallet.Currency = models.InternalCurrencyKey
}
var updatedAccount *models.Account
if request.Account.Wallet.Currency == models.InternalCurrencyKey {
accountx, err := receiver.accountRepo.ChangeWallet(ctx.Context(), request.Account.UserID, &models.Wallet{
Cash: request.Account.Wallet.Cash - request.Money,
Money: request.Account.Wallet.Money - request.Money,
Spent: request.Account.Wallet.Spent + request.Money,
PurchasesAmount: request.Account.Wallet.PurchasesAmount,
Currency: request.Account.Wallet.Currency,
})
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
updatedAccount = accountx
} else {
cash, err := receiver.currencyClient.Translate(ctx.Context(), &models.TranslateCurrency{
Money: request.Money,
From: models.InternalCurrencyKey,
To: request.Account.Wallet.Currency,
})
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
accountx, err := receiver.accountRepo.ChangeWallet(ctx.Context(), request.Account.UserID, &models.Wallet{
Cash: request.Account.Wallet.Cash - cash,
Money: request.Account.Wallet.Money - request.Money,
Spent: request.Account.Wallet.Spent + request.Money,
PurchasesAmount: request.Account.Wallet.PurchasesAmount,
Currency: request.Account.Wallet.Currency,
})
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
updatedAccount = accountx
}
if _, err := receiver.historyRepo.Insert(ctx.Context(), &models.History{
Key: models.CustomerHistoryKeyPayCart,
UserID: account.UserID,
Comment: "Успешная оплата корзины",
RawDetails: models.RawDetails{
Tariffs: tariffs,
Price: int64(discountResponse.Price),
},
}); err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
// TODO: обработать ошибки при отправке сообщений
sendErrors := make([]errors.Error, 0)
waitGroup := sync.WaitGroup{}
mutex := sync.Mutex{}
for _, tariff := range tariffs {
waitGroup.Add(1)
go func(currentTariff models.Tariff) {
defer waitGroup.Done()
if err := receiver.producer.Send(ctx.Context(), userID, &currentTariff); err != nil {
mutex.Lock()
defer mutex.Unlock()
sendErrors = append(sendErrors, err)
}
}(tariff)
}
waitGroup.Wait()
if len(sendErrors) > 0 {
for _, err := range sendErrors {
receiver.logger.Error("failed to send tariffs to broker on <Pay> of <CartService>", zap.Error(err))
}
return receiver.middleWare.Error(ctx, fiber.StatusInternalServerError, "failed to send tariffs to broker")
}
if _, err := receiver.accountRepo.ClearCart(ctx.Context(), account.UserID); err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
updatedAccount.Cart = []string{}
2024-05-23 14:34:45 +00:00
hlogger.Emit(models.InfoPayCart{
CtxUserIP: ctx.IP(),
CtxUserPort: ctx.Port(),
KeyDomain: strings.Join(ctx.Subdomains(), "/"),
KeyPath: ctx.Path(),
2024-05-23 14:34:45 +00:00
CtxUserID: userID,
CtxAccountID: updatedAccount.ID,
KeySuccess: true,
CtxPrice: discountResponse.Price,
CtxTariff: strings.Join(account.Cart, ","),
CtxDiscount: strings.Join(utils.GetAppliedDiscountsIDs(discountResponse.AppliedDiscounts), ","),
CtxRowPrice: tariffsAmount,
CtxRowData: utils.MarshalRawDetails(models.RawDetails{Tariffs: tariffs, Price: int64(discountResponse.Price)}),
})
return ctx.Status(fiber.StatusOK).JSON(updatedAccount)
}