using AyCode.Core.Extensions; using AyCode.Utils.Extensions; using Nop.Plugin.Misc.AuctionPlugin.Hubs.Messages; using Nop.Plugin.Misc.AuctionPlugin.Models; using Nop.Plugin.Misc.AuctionPlugin.Services; using Nop.Services.Catalog; using Nop.Services.Logging; using Microsoft.AspNetCore.SignalR; using Nop.Core; using Nop.Core.Domain.Catalog; using Nop.Plugin.Misc.AuctionPlugin.Domains.Enums; using Nop.Core.Domain.Customers; using Nop.Plugin.Misc.AuctionPlugin.Domains.Entities; using Nop.Services.Customers; using DocumentFormat.OpenXml.Wordprocessing; using Mango.Nop.Core.Repositories; using Nop.Core.Caching; namespace Nop.Plugin.Misc.AuctionPlugin.Hubs { //- Dollár currency //- Pipa, ha tied a licit //- Pause, Lezárás, Revert, //- ha saját licit a legjobb vagy lezárt, ne lehessen bid-elni //- az előző esetben a kliensen a gombot is tiltani, már a.cshtml-ben ellenőrizni! //- csak a watch-olt item-eknél legyen announcment //- ha bid-elt 1x is, kerüljön a watch-ba //- DbTransaction-t vhogy megcsinánli! public class SignalRMessageHandler(ILogger logger, IProductService productService, AuctionService auctionService, IHubContext hubContext, IWorkContext workContext, ICustomerService customerService) { private readonly Mutex _handleMessageMutex = new(); public async Task HandleMessage(MessageWrapper message) { var customer = await workContext.GetCurrentCustomerAsync(); if (message?.Data == null) { logger.Error($"SignalRMessageHandler.HandleMessage(); message?.Data == null", null, customer); return; } await logger.InformationAsync($"SignalRMessageHandler.HandleMessage(); jsonData: {message.Data}", null, customer); if (customer == null || message.SenderId <= 0 || message.SenderId != customer.Id || await customerService.IsGuestAsync(customer)) { logger.Error($"SignalRMessageHandler.HandleMessage(); (customer == null || message.SenderId <= 0 || message.SenderId != customer.Id || IsGuestAsync() == true)", null, customer); return; } using (_handleMessageMutex.UseWaitOne()) { try { if (message.MessageType == "BidRequestMessage") await HandleBidRequest(customer, message.Data.JsonTo()); else { if (!await customerService.IsAdminAsync(customer)) { logger.Error($"SignalRMessageHandler.HandleProductToAuctionStatusChangedRequest(); IsAdminAsync() == false!", null, customer); return; } switch (message.MessageType) { case nameof(AuctionProductStatusRequest): await HandleProductToAuctionStatusChangedRequest(customer, message.Data.JsonTo()); break; case nameof(RevertAuctionBidRequest): await HandleRevertAuctionBidRequest(customer, message.Data.JsonTo()); break; default: // Add other message types here await logger.ErrorAsync($"SignalRMessageHandler.HandleMessage(); Unknown message type; MessageType: {message.MessageType}", null, customer); break; } } } catch (Exception ex) { logger.Error($"SignalRMessageHandler.HandleMessage(); switch (message.MessageType); MessageType: {message.MessageType}; Exception: {ex.Message}", ex, customer); } } } private async Task HandleRevertAuctionBidRequest(Customer customer, RevertAuctionBidRequest revertAuctionBidRequest) { if (revertAuctionBidRequest == null) { logger.Error($"SignalRMessageHandler.HandleRevertAuctionBidRequest(); auctionProductStatusRequest == null", null, customer); return; } try { var productToAuction = (await auctionService.GetProductToAuctionMappingByIdAsync(revertAuctionBidRequest.ProductToAuctionId)); if (productToAuction is not { AuctionStatus: AuctionStatus.Pause }) { logger.Warning($"SignalRMessageHandler.HandleRevertAuctionBidRequest(); (productToAuction is not {{ AuctionStatus: AuctionStatus.Pause }}); AuctionStatus: {productToAuction?.AuctionStatus}", null, customer); return; //TODO: - J. } var product = await auctionService.GetProductById(productToAuction.ProductId); if (product == null) return; var revertLastBid = await auctionService.RevertAuctionBidByProductToAuctionIdAsync(productToAuction.Id); if (revertLastBid == null) { //TODO: NA ILYENKOR VAN A CUMI... - J. logger.Warning($"SignalRMessageHandler.HandleRevertAuctionBidRequest(); revertLastBid == null", null, customer); return; } await SetAuctionBidPrice(revertLastBid.BidPrice, productToAuction, product, revertLastBid.CustomerId); await SendAuctionBidMessageAsync(revertLastBid, productToAuction, product, customer.Id); } catch (Exception ex) { logger.Error($"SignalRMessageHandler.HandleRevertAuctionBidRequest(); Exception: {ex.Message}", ex, customer); } } private async Task HandleProductToAuctionStatusChangedRequest(Customer customer, AuctionProductStatusRequest auctionProductStatusRequest) { if (auctionProductStatusRequest == null) { logger.Error($"SignalRMessageHandler.HandleProductToAuctionStatusChangedRequest(); auctionProductStatusRequest == null", null, customer); return; } try { await logger.InformationAsync($"SignalRMessageHandler.HandleProductToAuctionStatusChangedRequest(); ProductToAuctionMappingId: {auctionProductStatusRequest.ProductToAuctionId}; Status: {auctionProductStatusRequest.AuctionStatus}({(int)auctionProductStatusRequest.AuctionStatus})", null, customer); var auction = await auctionService.GetAuctionDtoByProductToAuctionIdAsync(auctionProductStatusRequest.ProductToAuctionId, false); if (auction == null || auction.Closed) { logger.Error($"SignalRMessageHandler.HandleProductToAuctionStatusChangedRequest(); (auction == null || auction.Closed); Closed: {auction?.Closed}", null, customer); return; } var productToAuction = await auctionService.GetProductToAuctionMappingByIdAsync(auctionProductStatusRequest.ProductToAuctionId); if (productToAuction == null) { logger.Error($"SignalRMessageHandler.HandleProductToAuctionStatusChangedRequest(); (productToAuction == null)", null, customer); return; } if (!IsValidRequestAuctionStatus(auctionProductStatusRequest.AuctionStatus, productToAuction.AuctionStatus)) { logger.Error($"SignalRMessageHandler.HandleProductToAuctionStatusChangedRequest(); RequestAuctionStatusIsValid() == false; newStatus: {auctionProductStatusRequest.AuctionStatus}; oldStatus: {productToAuction.AuctionStatus}", null, customer); return; } if (auctionProductStatusRequest.AuctionStatus == AuctionStatus.None) { productToAuction.AuctionStatus = AuctionStatus.None; await auctionService.ResetProductToAuctionAsync(productToAuction, productToAuction.StartingPrice); } else { switch (auctionProductStatusRequest.AuctionStatus) { case AuctionStatus.Pause: productToAuction.AuctionStatus = AuctionStatus.Pause; break; case AuctionStatus.Sold: var lastAuctionBid = await auctionService.GetLastAuctionBidByProductToAuctionIdAsync(productToAuction.Id); if (lastAuctionBid == null) { productToAuction.AuctionStatus = AuctionStatus.NotSold; break; } productToAuction.AuctionStatus = AuctionStatus.Sold; productToAuction.CurrentPrice = lastAuctionBid.BidPrice; productToAuction.WinnerCustomerId = lastAuctionBid.CustomerId; break; case AuctionStatus.NotSold: logger.Error($"SignalRMessageHandler.HandleProductToAuctionStatusChangedRequest(); (auctionProductStatusRequest.AuctionStatus == AuctionStatus.NotSold)", null, customer); break; case AuctionStatus.FirstWarning: case AuctionStatus.SecondWarning: case AuctionStatus.Active: default: productToAuction.AuctionStatus = auctionProductStatusRequest.AuctionStatus; break; } await auctionService.UpdateProductToAuctionMappingAsync(productToAuction); } var productToauctionChangedNotification = new MessageWrapper { MessageType = nameof(ProductToAuctionStatusNotification), SenderId = customer.Id, Data = new ProductToAuctionStatusNotification(await auctionService.GetAuctionDtoByProductToAuctionIdAsync(productToAuction.Id, true)) { ToasterMessage = "EMPTY", //TODO: - J. }.ToJson() }; await hubContext.Clients.All.SendAsync("send", productToauctionChangedNotification.ToJson()); } catch (Exception ex) { logger.Error($"SignalRMessageHandler.HandleProductToAuctionStatusChangedRequest(); Exception: {ex.Message}", ex, customer); } } private async Task HandleBidRequest(Customer customer, AuctionBidRequest bidRequestMessage) { if (bidRequestMessage == null) { logger.Error($"SignalRMessageHandler.HandleBidRequest(); (bidRequestMessage == null)", null, customer); return; } try { await logger.InformationAsync($"SignalRMessageHandler.HandleBidRequest(); Bid received; Auction: {bidRequestMessage.AuctionId}; ProductToAuction: {bidRequestMessage.ProductAuctionMappingId}; Product: {bidRequestMessage.ProductId}; Bid: {bidRequestMessage.BidPrice}; Customer: {bidRequestMessage.CustomerId}", null, customer); var auction = await auctionService.GetAuctionDtoByIdAsync(bidRequestMessage.AuctionId, false, false); if (auction == null || auction.Closed) { logger.Warning($"SignalRMessageHandler.HandleBidRequest(); (auction == null || auction.Closed); Closed: {auction?.Closed}", null, customer); return; } var activeProductAuction = (await auctionService.GetProductToAuctionByAuctionIdAndProductIdAsync(bidRequestMessage.AuctionId, bidRequestMessage.ProductId, true)).FirstOrDefault(); if (activeProductAuction is not { IsActiveItem: true } || (activeProductAuction.WinnerCustomerId == customer.Id && !await customerService.IsAdminAsync(customer))) { logger.Warning($"SignalRMessageHandler.HandleBidRequest(); (activeProductAuction is not {{ IsActiveItem: true }} || activeProductAuction.WinnerCustomerId == customer.Id && !await customerService.IsAdminAsync(customer)); AuctionStatus: {activeProductAuction?.AuctionStatus}; WinnerCustomerId: {activeProductAuction?.WinnerCustomerId}", null, customer); return; //TODO: - J. } var product = await auctionService.GetProductById(bidRequestMessage.ProductId); if (product == null) return; var auctionBid = bidRequestMessage.CreateMainEntity(); auctionBid.ProductAuctionMappingId = activeProductAuction.Id; //TODO: Validálni, h a bejövő BidPrice valóban a következő licit lépcső! - J. if (product.Price >= auctionBid.BidPrice || activeProductAuction.CurrentPrice >= auctionBid.BidPrice) { logger.Warning($"SignalRMessageHandler.HandleBidRequest(); (product.Price >= bidRequestMessage.BidPrice || activeProductAuction.CurrentPrice >= auctionBid.BidPrice); productPrice: {product.Price}; bidRequestPrice: {auctionBid.BidPrice}; activeProductAuctionPrice: {activeProductAuction.CurrentPrice}", null, customer); return; } await auctionService.InsertBidAsync(auctionBid); activeProductAuction.AuctionStatus = AuctionStatus.Active; if (!await SetAuctionBidPrice(bidRequestMessage.BidPrice, activeProductAuction, product, customer.Id)) return; await SendAuctionBidMessageAsync(auctionBid, activeProductAuction, product, customer.Id); } catch (Exception ex) { logger.Error($"SignalRMessageHandler.HandleBidRequest(); Exception: {ex.Message}", ex, customer); } } private async Task SendAuctionBidMessageAsync(AuctionBid auctionBid, ProductToAuctionMapping productToAuction, Product product, int customerId) { var stepAmount = AuctionService.GetStepAmount(auctionBid.BidPrice); var nextBidPrice = auctionBid.BidPrice + stepAmount; var bidMessage = new MessageWrapper { MessageType = "bidNotification", SenderId = customerId, Data = new BidNotificationMessage(await auctionService.GetAuctionDtoByProductToAuctionIdAsync(productToAuction.Id, true)) //TODO: NE KÉRJÜK LE DB-BŐL!!! - j. { ProductName = product.Name, CurrentPrice = auctionBid.BidPrice, NextStepAmount = stepAmount, NextBidPrice = nextBidPrice, ToasterMessage = "EMPTY", //TODO: - J. }.ToJson() }; await hubContext.Clients.All.SendAsync("send", bidMessage.ToJson()); } private async Task SetAuctionBidPrice(decimal bidPrice, ProductToAuctionMapping productToAuction, Product product, int lastBidCustomerId) { try { product.Price = bidPrice; await productService.UpdateProductAsync(product); //activeProductAuction.StartingPrice = product.OldPrice; //TODO: ez biztosan kezelve van mikor összerendeljük? - J. productToAuction.CurrentPrice = product.Price; productToAuction.WinnerCustomerId = lastBidCustomerId; await auctionService.UpdateProductToAuctionMappingAsync(productToAuction); return true; } catch (Exception ex) { logger.Error($"SignalRMessageHandler.HandleBidRequest(); SetAuctionBidPrice() == false", ex); } return false; } private static bool IsValidRequestAuctionStatus(AuctionStatus newStatus, AuctionStatus oldStatus) { switch (oldStatus) { case AuctionStatus.None: return newStatus is AuctionStatus.Active or AuctionStatus.Pause; case AuctionStatus.Active: return newStatus is AuctionStatus.FirstWarning or AuctionStatus.Pause; case AuctionStatus.FirstWarning: return newStatus is AuctionStatus.SecondWarning or AuctionStatus.Pause; case AuctionStatus.SecondWarning: return newStatus is AuctionStatus.Sold or AuctionStatus.Pause; case AuctionStatus.Pause: return newStatus is AuctionStatus.None or AuctionStatus.Active; case AuctionStatus.Sold: case AuctionStatus.NotSold: default: return false; } } } }