Шаблон ASP.NET Синхронизация данных между двумя клиентами - PullRequest
0 голосов
/ 28 октября 2019

У меня есть сборка интернет-магазина на ASP.NET Boilerplate. (Угловой интерфейс и база данных MSSQL). В интернет-магазине представлены предметы, и я хочу вести инвентаризацию этих предметов. Каждый раз, когда создается заказ, инвентарь обновляется. Так что в основном у меня есть база данных с интернет-магазинами, товарами и заказами. У меня есть репозитории и менеджеры для этих объектов.

Все работает нормально, но проблема возникает, когда два клиента одновременно загружают интернет-магазин.

Client1 открывает веб-страницу:

  • Интернет-магазин1
    • Элемент 1: «10 элементов доступно»
    • Элемент 2: «8 элементов доступно»

Клиент2 открывает веб-страницу нав то же время:

  • Интернет-магазин1
    • Item1: "10 предметов в наличии"
    • Item2: "8 предметов в наличии"

Первый, купивший все доступные предметы, должен иметь возможность создать заказ, а второй должен получить ошибку.

При создании заказа сервер проверяет, достаточно ли предметов. доступный. Но когда интернет-магазин загружен ДО создания первого клиента, второй клиент не знает обновленного инвентаря и также сможет создать заказ.
Это означает, что 20 предметов Item1 могут быть проданы!

Как мне «синхронизировать» данные между двумя сеансами в бэкэнде? Кажется, что каким-то образом данные кэшируются в бэкэнде при загрузке интернет-магазина.

CreateOrderFunction

 public async Task<CreateOrderResponseDto> Create(CreateOrderDto input, long? userId)
        {

            input.OrderItems.ForEach(async o =>
            {
                if (!(await _salesItemManager.ReserveStock(o.SalesItemId, o.Quantity)).IsSuccess)
                {
                    throw CodeException.ToAbpValidationException("OrderItem", "OrderItemCreate");
                }
            });


            var salesPage = await _salesPageManager.Get(input.SalesPageId, false);

            if (salesPage.GetState() != StatePage.Published)
            {
                throw CodeException.ToAbpValidationException("Order", "PageNotAvailable");
            }

            if (salesPage.CommentsRequired.HasValue && salesPage.CommentsRequired.Value)
            {
                if (string.IsNullOrWhiteSpace(input.Description))
                {
                    throw CodeException.ToAbpValidationException("Order", "CommentsRequired");
                }
            }

            var order = new Order
            {
                Address = input.Address,
                City = input.City,
                LastName = input.LastName,
                Name = input.Name,
                PostalCode = input.PostalCode,
                Email = input.Email,
                PhoneNumber = input.PhoneNumber,
                Description = input.Description,
                SalesPage = salesPage
            };


            try
            {
                order.Price = await _salesItemManager.GetPriceByOrders(input.OrderItems);
                order = await _orderRepository.InsertAsync(order);

                input.OrderItems.ForEach(async o =>
                {
                    var orderItem = new OrderItem();
                    orderItem.SalesItemId = o.SalesItemId;
                    orderItem.OrderId = order.Id;
                    orderItem.Quantity = o.Quantity;

                    await _orderItemRepository.InsertAsync(orderItem);
                });

                if (input.SelectedSalesPageOptionId.HasValue)
                {
                    order.SalesOption = await _salesPageManager.GetOption(input.SelectedSalesPageOptionId.Value);
                }

            }
            catch (Exception e)
            {
                throw CodeException.ToAbpValidationException("OrderItem", "OrderItemCreate");
            }

            if (userId.HasValue && salesPage.User.Id == userId.Value)
            {
                var payment = await _paymentManager.CreateManualPayment(order, input.IsPaid);
                order.Payment = payment;

                return new CreateOrderResponseDto() { IsSuccess = true, PaymentUrl = string.Empty, OrderId = order.Id.ToString() };
            }
            else
            {
                var payment = await _paymentManager.CreatePayment(order);
                order.Payment = payment;
                return new CreateOrderResponseDto() { IsSuccess = true, PaymentUrl = payment.PaymentUrl, OrderId = order.Id.ToString() };
            }
        }
    }

ReserveStockFuntion

        public async Task<GeneralDto> ReserveStock(Guid itemId, int quantity)
        {
            var salesItem = await _salesItemRepository.GetAsync(itemId);

            if (salesItem == null || salesItem.Stock == null || salesItem.ReservedStock == null)
                return new GeneralDto() { IsSuccess = false };

            if (salesItem.Stock < quantity)
            {
                return new GeneralDto() { IsSuccess = false };
            }

            salesItem.Stock -= quantity;
            salesItem.ReservedStock += quantity;

            try
            {
                await _salesItemRepository.UpdateAsync(salesItem);
            }
            catch (Exception e)
            {
                throw CodeException.ToAbpValidationException("SalesItem", "SalesItemUpdate");
            }

            return new GeneralDto() { IsSuccess = true };
        }
...