package cart import ( "context" "fmt" "log" "time" "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/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" ) type accountRepository interface { FindByUserID(ctx context.Context, id string) (*models.Account, errors.Error) AddItemToCart(ctx context.Context, userID, itemID string) (*models.Account, errors.Error) RemoveItemFromCart(ctx context.Context, userID, itemID string) (*models.Account, errors.Error) ClearCart(ctx context.Context, userID string) (*models.Account, errors.Error) } type hubadminClient interface { GetTariff(ctx context.Context, accessToken, tariffID string) (*models.Tariff, errors.Error) GetTariffs(ctx context.Context, accessToken string, tarriffIDs []string) ([]models.Tariff, errors.Error) } type discountClient interface { Apply(context.Context, *discount.ApplyDiscountRequest) (*discount.ApplyDiscountResponse, errors.Error) } type walletService interface { ReplenishAccountWallet(context.Context, *models.ReplenishAccountWallet) (*models.Account, errors.Error) WithdrawAccountWalletMoney(context.Context, *models.WithdrawAccountWallet) (*models.Account, errors.Error) } type historyService interface { CreateHistory(ctx context.Context, history *models.History) (*models.History, errors.Error) } type tariffBrokerService interface { SendMany(ctx context.Context, userID string, tariffs []models.Tariff) []errors.Error } type Deps struct { Logger *zap.Logger Repository accountRepository HubadminClient hubadminClient DiscountClient discountClient WalletService walletService HistoryService historyService TariffBrokerService tariffBrokerService } type Service struct { logger *zap.Logger repository accountRepository hubadminClient hubadminClient discountClient discountClient walletService walletService historyService historyService tariffBrokerService tariffBrokerService } func New(deps Deps) *Service { if deps.Logger == nil { log.Panicln("logger is nil on ") } if deps.Repository == nil { log.Panicln("repository is nil on ") } if deps.HubadminClient == nil { log.Panicln("HubadminClient is nil on ") } if deps.DiscountClient == nil { log.Panicln("DiscountClient is nil on ") } if deps.WalletService == nil { log.Panicln("WalletService is nil on ") } if deps.HistoryService == nil { log.Panicln("HistoryService is nil on ") } if deps.TariffBrokerService == nil { log.Panicln("TariffBrokerService is nil on ") } return &Service{ logger: deps.Logger, repository: deps.Repository, hubadminClient: deps.HubadminClient, discountClient: deps.DiscountClient, walletService: deps.WalletService, historyService: deps.HistoryService, tariffBrokerService: deps.TariffBrokerService, } } func (receiver *Service) Remove(ctx context.Context, userID, itemID string) ([]string, errors.Error) { account, err := receiver.repository.RemoveItemFromCart(ctx, userID, itemID) if err != nil { receiver.logger.Error("failed to remove item from cart on of ", zap.Error(err)) return []string{}, err } return account.Cart, nil } func (receiver *Service) Add(ctx context.Context, request *models.AddItemToCart) ([]string, errors.Error) { tariff, err := receiver.hubadminClient.GetTariff(ctx, request.AccessToken, request.TariffID) if err != nil { receiver.logger.Error("failed to get tariff on of ", zap.Error(err), zap.String("tariffID", request.TariffID), zap.String("accessToken", request.AccessToken), ) return []string{}, err } if tariff == nil { return []string{}, errors.New( fmt.Errorf("failed to get tariff <%s> on of : tariff not found", request.TariffID), errors.ErrNotFound, ) } account, err := receiver.repository.AddItemToCart(ctx, request.UserID, request.TariffID) if err != nil { receiver.logger.Error("failed to add item to cart on of ", zap.Error(err)) return []string{}, err } return account.Cart, nil } func (receiver *Service) Pay(ctx context.Context, accessToken string, userID string) errors.Error { account, err := receiver.repository.FindByUserID(ctx, userID) if err != nil { receiver.logger.Error("failed to find account on of ", zap.String("userID", userID), zap.Error(err)) return err } receiver.logger.Debug("account for buy cart", zap.Any("trtr", account)) tariffs, err := receiver.hubadminClient.GetTariffs(ctx, accessToken, account.Cart) if err != nil { receiver.logger.Error("failed to get tarrifs on of ", zap.Strings("cart", account.Cart), zap.Error(err)) return err } receiver.logger.Debug("tariffs for buy cart", zap.Any("trtr", tariffs)) tariffsAmount := utils.CalculateCartPurchasesAmount(tariffs) receiver.logger.Debug("tariffsAmount for buy cart", zap.Any("trtr", tariffsAmount)) response, err := receiver.discountClient.Apply(ctx, &discount.ApplyDiscountRequest{ UserInformation: &discount.UserInformation{ ID: account.UserID, Type: string(account.Status), PurchasesAmount: uint64(account.Wallet.PurchasesAmount), CartPurchasesAmount: uint64(tariffsAmount), }, Products: transfer.TariffsToProductInformations(tariffs), Date: timestamppb.New(time.Now()), }) if err != nil { receiver.logger.Error("failed to discount on of ", zap.Error(err)) return err } receiver.logger.Debug("applyed discounts for buy cart", zap.Any("trtr", response)) if account.Wallet.Money < int64(response.Price) { receiver.logger.Error("insufficient funds on of ") return errors.New(fmt.Errorf("insufficient funds: %d", int64(response.Price)-account.Wallet.Money), errors.ErrInsufficientFunds) } if _, err := receiver.walletService.WithdrawAccountWalletMoney(ctx, &models.WithdrawAccountWallet{ Money: int64(response.Price), Account: account, }); err != nil { receiver.logger.Error("failed to withdraw money on of ", zap.Error(err)) return err } if _, historyErr := receiver.historyService.CreateHistory(ctx, &models.History{ Key: models.CustomerHistoryKeyPayCart, UserID: account.UserID, Comment: "Успешная оплата корзины", }); historyErr != nil { receiver.logger.Error("failed to insert history on of ", zap.Error(historyErr)) } // TODO: обработать ошибки при отправке сообщений if sendErrors := receiver.tariffBrokerService.SendMany(ctx, account.UserID, tariffs); len(sendErrors) > 0 { for _, err := range sendErrors { receiver.logger.Error("failed to send tariffs to broker on of ", zap.Error(err)) } return errors.NewWithMessage("failed to send tariffs to broker", errors.ErrInternalError) } if _, err := receiver.repository.ClearCart(ctx, account.UserID); err != nil { receiver.logger.Error("failed to clear cart on of ", zap.Error(err)) return err } return nil }