Example #1
0
        public async Task <Order> CreateOrderAsync(Guid userId, CreateOrderDTO orderRequest)
        {
            // проверяем, не пришел ли второй раз тот же самый запрос
            var existOrder = _orderContext.Orders.FirstOrDefault(g => g.IdempotencyKey == orderRequest.IdempotencyKey);

            if (existOrder != null)
            {
                return(existOrder);
            }

            Order order = null;
            bool  wasReserveProducts = false;

            try
            {
                var bucket = await _bucketRepository.GetBucketForUserAsync(userId);

                if (bucket == null || !bucket.Items.Any())
                {
                    return new Order
                           {
                               Status = OrderStatus.Error
                           }
                }
                ;

                // 1. расчитываем стоимость
                (decimal totalPrice, decimal discount)price = await CalculateTotalPriceAsync(bucket, userId);

                // 2. сохраняем заказ в БД
                var orderItems = bucket.Items.Select(g => new OrderItem
                {
                    ProductId = g.ProductId,
                    Quantity  = g.Quantity
                }).ToList();

                using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                {
                    order = new Order
                    {
                        CreatedOnUtc    = DateTime.UtcNow,
                        DeliveryAddress = orderRequest.DeliveryAddress,
                        Items           = orderItems,
                        Status          = OrderStatus.Pending,
                        UserId          = userId,
                        TotalPrice      = price.totalPrice
                    };

                    foreach (var item in orderItems)
                    {
                        item.Order = order;
                    }

                    _orderContext.Add(order);
                    await _orderContext.SaveChangesAsync();

                    await _bucketRepository.ClearBucketAsync(userId);

                    scope.Complete();
                }
                // 3. резервируем на складе
                _warehouseServiceClient.AddHeader(Constants.USERID_HEADER, userId.ToString());
                var reserve = await _warehouseServiceClient.ReserveAsync(new ReserveProductRequestDTO
                {
                    OrderNumber = order.OrderNumber.ToString(),
                    Products    = order.Items.Select(g => new ReserveProductDTO
                    {
                        Id    = g.ProductId,
                        Count = g.Quantity
                    }).ToList()
                });

                wasReserveProducts = true;
                if (reserve.IsSuccess == false)
                {
                    throw new Exception("Не удалось полностью зарезервировать товар");
                }
            }
            catch (Exception ex)
            {
                if (wasReserveProducts)
                {
                    try
                    {
                        await _warehouseServiceClient.CancelAsync(order.OrderNumber.ToString());
                    }
                    catch (Exception inEx)
                    {
                        // логируем, что товар не сняли с резервирования
                    }
                }

                try
                {
                    if (order != null)
                    {
                        existOrder                  = _orderContext.Orders.FirstOrDefault(g => g.OrderNumber == order.OrderNumber);
                        existOrder.Status           = OrderStatus.Error;
                        existOrder.ErrorDescription = ex.Message;
                        await _orderContext.SaveChangesAsync();
                    }
                }
                catch (Exception inEx)
                {
                    // логируем, что не удалось изменить статус
                }
                _metricReporter.RegisterFailedOrder();
            }
            return(order);
        }