Mango.Nop.Plugins/Nop.Plugin.Misc.AuctionPlugin/Hubs/SignalRMessageHandler.cs

407 lines
22 KiB
C#

using AyCode.Core.Extensions;
using AyCode.Utils.Extensions;
using Nop.Plugin.Misc.AuctionPlugin.Hubs.Messages;
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;
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<AuctionHub> hubContext, IWorkContext workContext, ICustomerService customerService, ICategoryService categoryService)
{
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(); MessageType: {message.MessageType}; 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;
}
//TODO: az összes request-et egy base-ből származtatni és beletenni az AuctionRequestMode-ot! - J.
using (_handleMessageMutex.UseWaitOne())
{
try
{
if (message.MessageType == "BidRequestMessage") await HandleBidRequest(customer, message.Data.JsonTo<AuctionBidRequest>());
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<AuctionProductStatusRequest>());
break;
case nameof(RevertAuctionBidRequest):
await HandleRevertAuctionBidRequest(customer, message.Data.JsonTo<RevertAuctionBidRequest>());
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;
}
await logger.InformationAsync($"SignalRMessageHandler.HandleRevertAuctionBidRequest(); (productToAuction is not {{ AuctionStatus: AuctionStatus.Pause }}); AuctionStatus: {productToAuction.AuctionStatus}", null, customer);
var product = await auctionService.GetProductById(productToAuction.ProductId);
if (product == null) return;
var revertLastBid = await auctionService.RevertAuctionBidByProductToAuctionIdAsync(productToAuction.Id);
if (revertLastBid == null) await ResetProductToAuction(productToAuction);
else
{
productToAuction.BidsCount = await auctionService.GetBidsCountByProductToAuctionIdAsync(productToAuction.Id);
await SetAuctionBidPrice(revertLastBid.BidPrice, productToAuction, product, revertLastBid.CustomerId);
}
await SendAuctionBidMessageAsync(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 productToAuction = await auctionService.GetProductToAuctionMappingByIdAsync(auctionProductStatusRequest.ProductToAuctionId);
if (productToAuction == null)
{
logger.Error($"SignalRMessageHandler.HandleProductToAuctionStatusChangedRequest(); (productToAuction == null)", null, customer);
return;
}
var auction = await auctionService.GetAuctionByIdAsync(productToAuction.AuctionId);
if (auction == null || auction.Closed)
{
logger.Error($"SignalRMessageHandler.HandleProductToAuctionStatusChangedRequest(); (auction == null || auction.Closed); Closed: {auction?.Closed}", 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) await ResetProductToAuction(productToAuction, auction.CategoryId);
else
{
switch (auctionProductStatusRequest.AuctionStatus)
{
case AuctionStatus.Active:
productToAuction.AuctionStatus = AuctionStatus.Active;
await UpdateProductCategoryIsFeaturedAsync(productToAuction.ProductId, auction.CategoryId, true);
break;
case AuctionStatus.FirstWarning:
case AuctionStatus.SecondWarning:
productToAuction.AuctionStatus = auctionProductStatusRequest.AuctionStatus;
break;
case AuctionStatus.Sold:
var lastAuctionBid = await auctionService.GetLastAuctionBidByProductToAuctionIdAsync(productToAuction.Id);
if (lastAuctionBid == null) productToAuction.AuctionStatus = AuctionStatus.NotSold;
else
{
productToAuction.AuctionStatus = AuctionStatus.Sold;
productToAuction.CurrentPrice = lastAuctionBid.BidPrice;
productToAuction.WinnerCustomerId = lastAuctionBid.CustomerId;
var placeOrderResult = await auctionService.CreateOrderForWinnerAsync(productToAuction);
if (placeOrderResult == null || placeOrderResult.Id == 0)
{
logger.Error($"SignalRMessageHandler.HandleProductToAuctionStatusChangedRequest(); (placeOrderResult == null || placeOrderResult.Id == 0)", null, customer);
//return; //TODO: EGYELŐRE HAGYJUK LEZÁRNI AKKOR IS, HA NEM SIKERÜLT AZ ORDERPLACE()! - J.
}
}
await UpdateProductCategoryIsFeaturedAsync(productToAuction.ProductId, auction.CategoryId, false);
break;
case AuctionStatus.Pause:
productToAuction.AuctionStatus = AuctionStatus.Pause;
break;
default:
logger.Error($"SignalRMessageHandler.HandleProductToAuctionStatusChangedRequest(); AuctionStatus not found; (auctionProductStatusRequest.AuctionStatus == {auctionProductStatusRequest.AuctionStatus})", null, customer);
return;
}
await auctionService.UpdateProductToAuctionMappingAsync(productToAuction);
}
var bidsCount = await auctionService.GetBidsCountByProductToAuctionIdAsync(productToAuction.Id);
var productToauctionChangedNotification = new MessageWrapper
{
MessageType = nameof(ProductToAuctionStatusNotification),
SenderId = customer.Id,
Data = new ProductToAuctionStatusNotification(await auctionService.GetAuctionDtoByProductToAuctionIdAsync(productToAuction.Id, true), bidsCount, "EMPTY").ToJson()
};
await hubContext.Clients.All.SendAsync("send", productToauctionChangedNotification.ToJson());
//TODO: gond van ha az Admin valamit módosít és újrazár egy régi ProductToAuction-t!!!! AuctionRequestMode.Normal...- J.
if (/*AuctionRequestMode.Normal &&*/ auction.AuctionType == AuctionType.AutomaticNext && productToAuction.AuctionStatus is AuctionStatus.Sold or AuctionStatus.NotSold)
await StepNextProductToAuctionAsync(customer, productToAuction, auctionProductStatusRequest);
}
catch (Exception ex)
{
logger.Error($"SignalRMessageHandler.HandleProductToAuctionStatusChangedRequest(); Exception: {ex.Message}", ex, customer);
}
}
private async Task StepNextProductToAuctionAsync(Customer customer, ProductToAuctionMapping productToAuction, AuctionProductStatusRequest auctionProductStatusRequest)
{
var nextProductToAuction = await auctionService.GetNextProductToAuctionByAuctionIdAsync(productToAuction.AuctionId);
if (nextProductToAuction == null) return;
await logger.InformationAsync($"SignalRMessageHandler.StepNextProductToAuctionAsync(); NextProductToAuctionId: {nextProductToAuction.Id};", null, customer);
if (nextProductToAuction.AuctionStatus != AuctionStatus.None)
{
await logger.WarningAsync($"SignalRMessageHandler.StepNextProductToAuctionAsync(); (nextProductToAuction.AuctionStatus != AuctionStatus.None); NextProductToAuctionId: {nextProductToAuction.Id}; Status: {nextProductToAuction.AuctionStatus}({(int)nextProductToAuction.AuctionStatus})", null, customer);
return; //TODO: Ilyenkor mi legyen?! - J.
}
auctionProductStatusRequest.AuctionStatus = AuctionStatus.Active;
auctionProductStatusRequest.ProductToAuctionId = nextProductToAuction.Id;
await HandleProductToAuctionStatusChangedRequest(customer, auctionProductStatusRequest);
}
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;
}
var product = await auctionService.GetProductById(bidRequestMessage.ProductId);
if (product == null) return;
if (!IsValidBidPrice(activeProductAuction.CurrentPrice, bidRequestMessage.BidPrice, activeProductAuction.BidsCount > 0, customer)) return;
var auctionBid = bidRequestMessage.CreateMainEntity();
auctionBid.ProductAuctionMappingId = activeProductAuction.Id;
await auctionService.InsertBidAsync(auctionBid);
activeProductAuction.BidsCount++;
activeProductAuction.AuctionStatus = AuctionStatus.Active;
if (!await SetAuctionBidPrice(auctionBid.BidPrice, activeProductAuction, product, customer.Id)) return;
await SendAuctionBidMessageAsync(activeProductAuction, product, customer.Id);
}
catch (Exception ex)
{
logger.Error($"SignalRMessageHandler.HandleBidRequest(); Exception: {ex.Message}", ex, customer);
}
}
private async Task<bool> 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 = bidPrice;
productToAuction.WinnerCustomerId = lastBidCustomerId;
await auctionService.UpdateProductToAuctionMappingAsync(productToAuction);
return true;
}
catch (Exception ex)
{
logger.Error($"SignalRMessageHandler.HandleBidRequest(); SetAuctionBidPrice() == false", ex);
}
return false;
}
private async Task SendAuctionBidMessageAsync(ProductToAuctionMapping productToAuction, Product product, int customerId)
{
var bidsCount = productToAuction.BidsCount;//await auctionService.GetBidsCountByProductToAuctionIdAsync(productToAuction.Id);
var stepAmount = GetStepAmount(productToAuction.CurrentPrice);
var nextBidPrice = GetNextBidPrice(productToAuction.CurrentPrice, stepAmount, bidsCount > 0);
var bidMessage = new MessageWrapper
{
MessageType = "bidNotification",
SenderId = customerId,
Data = new BidNotificationMessage(await auctionService.GetAuctionDtoByProductToAuctionIdAsync(productToAuction.Id, true), bidsCount, "EMPTY") //TODO: NE KÉRJÜK LE DB-BŐL!!! - J.
{
ProductName = product.Name,
CurrentPrice = productToAuction.CurrentPrice,
NextStepAmount = stepAmount,
NextBidPrice = nextBidPrice,
}.ToJson()
};
await hubContext.Clients.All.SendAsync("send", bidMessage.ToJson());
}
private async Task ResetProductToAuction(ProductToAuctionMapping productToAuction, int? categoryId = null)
{
productToAuction.AuctionStatus = AuctionStatus.None;
await auctionService.ResetProductToAuctionAsync(productToAuction, productToAuction.StartingPrice);
categoryId ??= (await auctionService.GetAuctionByIdAsync(productToAuction.AuctionId))?.CategoryId;
await UpdateProductCategoryIsFeaturedAsync(productToAuction.ProductId, categoryId, false);
}
private async Task UpdateProductCategoryIsFeaturedAsync(int productId, int? categoryId, bool isFeatured)
{
//Leszarjuk ha elszáll, az aukció menjen tovább... - J.
try
{
if (categoryId.GetValueOrDefault(0) == 0) return;
var productCategory = (await categoryService.GetProductCategoriesByProductIdAsync(productId)).FirstOrDefault(x => x.CategoryId == categoryId);
if (productCategory == null) return;
if (productCategory.IsFeaturedProduct == isFeatured) return;
productCategory.IsFeaturedProduct = isFeatured;
await categoryService.UpdateProductCategoryAsync(productCategory);
}
catch (Exception ex)
{
logger.Error($"SignalRMessageHandler.UpdateProductCategoryIsFeaturedAsync(); categoryId: {categoryId}; productId: {productId}; isFeatured: {isFeatured}", ex);
}
}
private bool IsValidBidPrice(decimal productToAuctionCurrentPrice, decimal bidRequestPrice, bool hasAuctionBidInDb, Customer customer = null)
{
var nextBidPrice = GetNextBidPrice(productToAuctionCurrentPrice, hasAuctionBidInDb);
if (bidRequestPrice != nextBidPrice)
{
logger.Warning($"SignalRMessageHandler.IsValidBidPrice(); (bidRequestPrice != nextBidPrice); productToAuctionCurrentPrice: {productToAuctionCurrentPrice}; bidRequestPrice: {bidRequestPrice}; hasAuctionBidInDb: {hasAuctionBidInDb}", null, customer);
return false;
}
return true;
}
private static decimal GetStepAmount(decimal currentBidPrice) => AuctionService.GetStepAmount(currentBidPrice);
private static decimal GetNextBidPrice(decimal currentBidPrice, bool hasAuctionBidInDb) => GetNextBidPrice(currentBidPrice, GetStepAmount(currentBidPrice), hasAuctionBidInDb);
private static decimal GetNextBidPrice(decimal currentBidPrice, decimal stepAmount, bool hasAuctionBidInDb) => AuctionService.GetNextBidPrice(currentBidPrice, stepAmount, hasAuctionBidInDb);
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;
}
}
}
}