generated from PenaSide/GolangTemplate
307 lines
10 KiB
Go
307 lines
10 KiB
Go
package cart_client
|
|
|
|
import (
|
|
"github.com/gofiber/fiber/v2"
|
|
"go.uber.org/zap"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
"penahub.gitlab.yandexcloud.net/backend/penahub_common/log_mw"
|
|
"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"
|
|
"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,
|
|
}
|
|
}
|
|
|
|
func (receiver *CartController) Remove(ctx *fiber.Ctx) error {
|
|
userID, ok := receiver.middleWare.ExtractUserID(ctx)
|
|
if !ok || userID == "" {
|
|
return receiver.middleWare.NoAuth(ctx)
|
|
}
|
|
// 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)
|
|
}
|
|
|
|
hlogger := log_mw.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)
|
|
}
|
|
|
|
hlogger.Emit(models.InfoAddToCart{
|
|
CtxUserID: userID,
|
|
CtxAccountID: cartItems.ID,
|
|
CtxTariffID: tariffID,
|
|
})
|
|
|
|
return ctx.Status(fiber.StatusOK).JSON(cartItems)
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
hlogger := log_mw.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) {
|
|
hlogger.Emit(models.InfoPayCart{
|
|
CtxUserID: userID,
|
|
CtxAccountID: account.ID,
|
|
KeySuccess: uint8(0),
|
|
CtxPrice: int64(discountResponse.Price - uint64(account.Wallet.Money)),
|
|
CtxTariff: strings.Join(account.Cart, ","),
|
|
CtxDiscount: strings.Join(utils.GetAppliedDiscountsIDs(discountResponse.AppliedDiscounts), ","),
|
|
CtxRowPrice: int64(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, ¤tTariff); 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{}
|
|
|
|
hlogger.Emit(models.InfoPayCart{
|
|
CtxUserID: userID,
|
|
CtxAccountID: updatedAccount.ID,
|
|
KeySuccess: uint8(1),
|
|
CtxPrice: int64(discountResponse.Price),
|
|
CtxTariff: strings.Join(account.Cart, ","),
|
|
CtxDiscount: strings.Join(utils.GetAppliedDiscountsIDs(discountResponse.AppliedDiscounts), ","),
|
|
CtxRowPrice: int64(tariffsAmount),
|
|
CtxRowData: utils.MarshalRawDetails(models.RawDetails{Tariffs: tariffs, Price: int64(discountResponse.Price)}),
|
|
})
|
|
|
|
return ctx.Status(fiber.StatusOK).JSON(updatedAccount)
|
|
}
|