Browse Source

Improve FlashSales

pull/184/head
Jadyn 4 years ago
parent
commit
e7c4383c7e
  1. 7
      EShop.sln
  2. 14
      plugins/FlashSales/EasyAbp.EShop.Plugins.FlashSales.sln
  3. 3
      plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Orders/CreateFlashSalesOrderEventHandler.cs
  4. 33
      plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationAutoMapperProfile.cs
  5. 16
      plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationModule.cs
  6. 2
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/Dtos/FlashSalesPlanUpdateDto.cs
  7. 10
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesApplicationModule.cs
  8. 28
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesApplicationAutoMapperProfile.cs
  9. 115
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanAppService.cs
  10. 9
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanCacheInvalidator.cs
  11. 1
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanCacheItem.cs
  12. 1
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/CreateFlashSalesOrderCompleteEto.cs
  13. 1
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanEto.cs
  14. 31
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanHasher.cs
  15. 1
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductAttributeEto.cs
  16. 1
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductAttributeOptionEto.cs
  17. 5
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductEto.cs
  18. 6
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductSkuEto.cs
  19. 29
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesReduceInventoryEto.cs
  20. 9
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/IFlashSalesPlanHasher.cs
  21. 6
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/ProductDetailEto.cs
  22. 8
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesResults/FlashSalesResultFailedReason.cs
  23. 2
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesDomainModule.cs
  24. 2
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlan/ViewModels/EditFlashSalesPlanViewModel.cs
  25. 15
      plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp.EShop.Products.Plugins.FlashSales.Application.csproj
  26. 37
      plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesApplicationAutoMapperProfile.cs
  27. 28
      plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesApplicationModule.cs
  28. 113
      plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/FlashSalesReduceInventoryEventHandler.cs
  29. 3
      plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/FodyWeavers.xml
  30. 30
      plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/FodyWeavers.xsd
  31. 1
      samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSample.Application.csproj
  32. 3
      samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSampleApplicationModule.cs

7
EShop.sln

@ -413,6 +413,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Flash
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Orders.Plugins.FlashSales.Application", "plugins\FlashSales\src\EasyAbp.EShop.Orders.Plugins.FlashSales.Application\EasyAbp.EShop.Orders.Plugins.FlashSales.Application.csproj", "{5732E880-CB72-49A0-AC4F-A0620F4E4D16}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Products.Plugins.FlashSales.Application", "plugins\FlashSales\src\EasyAbp.EShop.Products.Plugins.FlashSales.Application\EasyAbp.EShop.Products.Plugins.FlashSales.Application.csproj", "{6AD2F468-D86C-4F9A-B280-3BCC15661C47}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -1071,6 +1073,10 @@ Global
{5732E880-CB72-49A0-AC4F-A0620F4E4D16}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5732E880-CB72-49A0-AC4F-A0620F4E4D16}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5732E880-CB72-49A0-AC4F-A0620F4E4D16}.Release|Any CPU.Build.0 = Release|Any CPU
{6AD2F468-D86C-4F9A-B280-3BCC15661C47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6AD2F468-D86C-4F9A-B280-3BCC15661C47}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6AD2F468-D86C-4F9A-B280-3BCC15661C47}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6AD2F468-D86C-4F9A-B280-3BCC15661C47}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1277,6 +1283,7 @@ Global
{68993D65-2C9C-438F-8B94-15E98C469A16} = {9C180C9E-50E9-4624-BE06-5C8C24A028E4}
{417AB8E2-1488-4814-9699-3B189D1ABA67} = {9C180C9E-50E9-4624-BE06-5C8C24A028E4}
{5732E880-CB72-49A0-AC4F-A0620F4E4D16} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B}
{6AD2F468-D86C-4F9A-B280-3BCC15661C47} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {28315BFD-90E7-4E14-A2EA-F3D23AF4126F}

14
plugins/FlashSales/EasyAbp.EShop.Plugins.FlashSales.sln

@ -43,6 +43,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Flash
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.FlashSales.Installer", "src\EasyAbp.EShop.Plugins.FlashSales.Installer\EasyAbp.EShop.Plugins.FlashSales.Installer.csproj", "{BE39FD00-745B-4049-8161-FC129817CBE4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Orders.Plugins.FlashSales.Application", "src\EasyAbp.EShop.Orders.Plugins.FlashSales.Application\EasyAbp.EShop.Orders.Plugins.FlashSales.Application.csproj", "{AC7079C6-FEFE-4277-844B-2C9B8C21432D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Products.Plugins.FlashSales.Application", "src\EasyAbp.EShop.Products.Plugins.FlashSales.Application\EasyAbp.EShop.Products.Plugins.FlashSales.Application.csproj", "{5711E14D-ADF0-4BAB-BDDB-28A663A07442}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -117,6 +121,14 @@ Global
{BE39FD00-745B-4049-8161-FC129817CBE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BE39FD00-745B-4049-8161-FC129817CBE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BE39FD00-745B-4049-8161-FC129817CBE4}.Release|Any CPU.Build.0 = Release|Any CPU
{AC7079C6-FEFE-4277-844B-2C9B8C21432D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AC7079C6-FEFE-4277-844B-2C9B8C21432D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AC7079C6-FEFE-4277-844B-2C9B8C21432D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AC7079C6-FEFE-4277-844B-2C9B8C21432D}.Release|Any CPU.Build.0 = Release|Any CPU
{5711E14D-ADF0-4BAB-BDDB-28A663A07442}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5711E14D-ADF0-4BAB-BDDB-28A663A07442}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5711E14D-ADF0-4BAB-BDDB-28A663A07442}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5711E14D-ADF0-4BAB-BDDB-28A663A07442}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -139,6 +151,8 @@ Global
{1EDCD6D4-DF3A-4E3B-ABB6-C0D0B373EAB8} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D}
{F6AC8D4A-EDD7-4514-8E8A-5BCB019864DB} = {E400416D-2895-4512-9D17-90681EEC7E0A}
{BE39FD00-745B-4049-8161-FC129817CBE4} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545}
{AC7079C6-FEFE-4277-844B-2C9B8C21432D} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545}
{5711E14D-ADF0-4BAB-BDDB-28A663A07442} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4324B3B4-B60B-4E3C-91D8-59576B4E26DD}

3
plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Orders/CreateFlashSalesOrderEventHandler.cs

@ -41,9 +41,6 @@ public class CreateFlashSalesOrderEventHandler : IDistributedEventHandler<Create
[UnitOfWork(true)]
public virtual async Task HandleEventAsync(CreateFlashSalesOrderEto eventData)
{
//How to check product is available?
//How to reduce product stock?
var input = new CreateOrderDto()
{
StoreId = eventData.StoreId,

33
plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationAutoMapperProfile.cs → plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationAutoMapperProfile.cs

@ -1,34 +1,35 @@
using System.Collections.Generic;
using System;
using AutoMapper;
using AutoMapper;
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans;
using EasyAbp.EShop.Products.ProductDetails.Dtos;
using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Products.Products.Dtos;
using Volo.Abp.AutoMapper;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
namespace EasyAbp.EShop.Orders
namespace EasyAbp.EShop.Orders.Plugins.FlashSales
{
public class EShopOrdersPluginsFlashSalesApplicationAutoMapperProfile : Profile, ISingletonDependency
public class EShopOrdersPluginsFlashSalesApplicationAutoMapperProfile : Profile
{
public EShopOrdersPluginsFlashSalesApplicationAutoMapperProfile(IJsonSerializer jsonSerializer)
public EShopOrdersPluginsFlashSalesApplicationAutoMapperProfile()
{
/* You can configure your AutoMapper mapping configuration here.
* Alternatively, you can split your mapping configurations
* into multiple profile classes for a better organization. */
CreateMap<FlashSalesProductEto, ProductDto>(MemberList.Source)
.ForSourceMember(x => x.TenantId, opt => opt.DoNotValidate())
CreateMap<FlashSalesProductEto, ProductDto>(MemberList.Destination)
.Ignore(dto => dto.Sold)
.Ignore(dto => dto.MinimumPrice)
.Ignore(dto => dto.MaximumPrice)
.MapExtraProperties();
CreateMap<FlashSalesProductSkuEto, ProductSkuDto>(MemberList.Source)
.ForMember(x => x.AttributeOptionIds, opt => opt.Ignore())
.AfterMap((src, dest) => dest.AttributeOptionIds = jsonSerializer.Deserialize<List<Guid>>(src.SerializedAttributeOptionIds))
CreateMap<FlashSalesProductSkuEto, ProductSkuDto>(MemberList.Destination)
.ForSourceMember(entity => entity.SerializedAttributeOptionIds, opt => opt.DoNotValidate())
.Ignore(dto => dto.DiscountedPrice)
.Ignore(dto => dto.Inventory)
.Ignore(dto => dto.Sold)
.MapExtraProperties();
CreateMap<FlashSalesProductAttributeEto, ProductAttributeDto>(MemberList.Source)
CreateMap<FlashSalesProductAttributeEto, ProductAttributeDto>(MemberList.Destination)
.MapExtraProperties();
CreateMap<FlashSalesProductAttributeOptionEto, ProductAttributeOptionDto>(MemberList.Source)
CreateMap<FlashSalesProductAttributeOptionEto, ProductAttributeOptionDto>(MemberList.Destination)
.MapExtraProperties();
CreateMap<FlashSalesProductDetailEto, ProductDetailDto>(MemberList.Source)
CreateMap<FlashSalesProductDetailEto, ProductDetailDto>(MemberList.Destination)
.MapExtraProperties();
}
}

16
plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationModule.cs → plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationModule.cs

@ -1,12 +1,12 @@
using EasyAbp.EShop.Orders;
using EasyAbp.EShop.Orders.Orders;
using EasyAbp.EShop.Orders.Orders;
using EasyAbp.EShop.Plugins.FlashSales;
using EasyAbp.EShop.Products;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AutoMapper;
using Volo.Abp.Modularity;
namespace EasyAbp.EShop.Plugins.FlashSales;
namespace EasyAbp.EShop.Orders.Plugins.FlashSales;
[DependsOn(
typeof(EShopOrdersApplicationModule),
@ -22,16 +22,10 @@ public class EShopOrdersPluginsFlashSalesApplicationModule : AbpModule
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAutoMapperObjectMapper<EShopOrdersPluginsFlashSalesApplicationAutoMapperProfile>();
context.Services.AddAutoMapperObjectMapper<EShopOrdersPluginsFlashSalesApplicationModule>();
Configure<AbpAutoMapperOptions>(options =>
{
options.Configurators.Add(abpAutoMapperConfigurationContext =>
{
var profile = abpAutoMapperConfigurationContext.ServiceProvider
.GetRequiredService<EShopOrdersPluginsFlashSalesApplicationAutoMapperProfile>();
abpAutoMapperConfigurationContext.MapperConfiguration.AddProfile(profile);
});
options.AddMaps<EShopOrdersPluginsFlashSalesApplicationModule>(validate: true);
});
}
}

2
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/Dtos/FlashSalesPlanUpdateDto.cs

@ -7,6 +7,8 @@ namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos;
[Serializable]
public class FlashSalesPlanUpdateDto : ExtensibleEntityDto, IHasConcurrencyStamp
{
public Guid StoreId { get; set; }
public DateTime BeginTime { get; set; }
public DateTime EndTime { get; set; }

10
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesApplicationModule.cs

@ -7,6 +7,7 @@ using Volo.Abp.Caching;
using Volo.Abp.Modularity;
namespace EasyAbp.EShop.Plugins.FlashSales;
[DependsOn(
typeof(EShopProductsApplicationContractsModule),
typeof(EShopPluginsFlashSalesDomainModule),
@ -23,14 +24,7 @@ public class EShopPluginsFlashSalesApplicationModule : AbpModule
context.Services.AddAutoMapperObjectMapper<EShopPluginsFlashSalesApplicationModule>();
Configure<AbpAutoMapperOptions>(options =>
{
options.Configurators.Add(abpAutoMapperConfigurationContext =>
{
var profile = abpAutoMapperConfigurationContext.ServiceProvider
.GetRequiredService<FlashSalesApplicationAutoMapperProfile>();
abpAutoMapperConfigurationContext.MapperConfiguration.AddProfile(profile);
});
options.AddMaps<EShopPluginsFlashSalesApplicationModule>(validate: true);
});
}
}

28
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesApplicationAutoMapperProfile.cs

@ -1,48 +1,28 @@
using System.Linq;
using AutoMapper;
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans;
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos;
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults;
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults.Dtos;
using EasyAbp.EShop.Products.ProductDetails.Dtos;
using EasyAbp.EShop.Products.Products.Dtos;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
namespace EasyAbp.EShop.Plugins.FlashSales;
public class FlashSalesApplicationAutoMapperProfile : Profile, ISingletonDependency
public class FlashSalesApplicationAutoMapperProfile : Profile
{
public FlashSalesApplicationAutoMapperProfile(IJsonSerializer jsonSerializer)
public FlashSalesApplicationAutoMapperProfile()
{
/* You can configure your AutoMapper mapping configuration here.
* Alternatively, you can split your mapping configurations
* into multiple profile classes for a better organization. */
CreateMap<FlashSalesPlan, FlashSalesPlanDto>()
.MapExtraProperties();
CreateMap<FlashSalesPlanDto, FlashSalesPlanEto>()
.ForMember(x => x.TenantId, opt => opt.Ignore())
.MapExtraProperties();
CreateMap<FlashSalesPlanCreateDto, FlashSalesPlan>(MemberList.Source);
CreateMap<FlashSalesPlanUpdateDto, FlashSalesPlan>(MemberList.Source);
CreateMap<FlashSalesPlan, FlashSalesPlanCacheItem>()
.MapExtraProperties();
CreateMap<FlashSalesResult, FlashSalesResultDto>()
CreateMap<FlashSalesPlanCacheItem, FlashSalesPlanEto>()
.MapExtraProperties();
CreateMap<ProductDto, FlashSalesProductEto>()
.ForMember(x => x.TenantId, opt => opt.Ignore())
.MapExtraProperties();
CreateMap<ProductSkuDto, FlashSalesProductSkuEto>()
.ForMember(x => x.SerializedAttributeOptionIds, opt => opt.Ignore())
.AfterMap((src, dest) => dest.SerializedAttributeOptionIds = jsonSerializer.Serialize(src.AttributeOptionIds.OrderBy(x => x)))
.MapExtraProperties();
CreateMap<ProductAttributeDto, FlashSalesProductAttributeEto>()
.MapExtraProperties();
CreateMap<ProductAttributeOptionDto, FlashSalesProductAttributeOptionEto>()
.MapExtraProperties();
CreateMap<ProductDetailDto, FlashSalesProductDetailEto>()
CreateMap<FlashSalesResult, FlashSalesResultDto>()
.MapExtraProperties();
}
}

115
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanAppService.cs

@ -1,16 +1,13 @@
using System;
using System.Linq;
using System.Numerics;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos;
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults;
using EasyAbp.EShop.Plugins.FlashSales.Permissions;
using EasyAbp.EShop.Products.ProductDetails;
using EasyAbp.EShop.Products.ProductDetails.Dtos;
using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Products.Products.Dtos;
using EasyAbp.EShop.Stores.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Caching.Distributed;
using Volo.Abp;
@ -22,7 +19,6 @@ using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Users;
using static EasyAbp.EShop.Products.Permissions.ProductsPermissions;
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans;
@ -38,15 +34,25 @@ public class FlashSalesPlanAppService :
protected override string DeletePolicyName { get; set; } = FlashSalesPermissions.FlashSalesPlan.Delete;
protected IFlashSalesPlanRepository FlashSalesPlanRepository { get; }
protected IProductAppService ProductAppService { get; }
protected IProductDetailAppService ProductDetailAppService { get; }
protected IDistributedCache TokenDistributedCache { get; }
protected IDistributedCache<FlashSalesPlanCacheItem, Guid> DistributedCache { get; }
protected IDistributedEventBus DistributedEventBus { get; }
protected FlashSalesPlanManager FlashSalesPlanManager { get; }
protected IFlashSalesResultRepository FlashSalesResultRepository { get; }
protected IAbpDistributedLock DistributedLock { get; }
protected IFlashSalesPlanHasher FlashSalesPlanHasher { get; }
public FlashSalesPlanAppService(
IFlashSalesPlanRepository flashSalesPlanRepository,
IProductAppService productAppService,
@ -56,7 +62,8 @@ public class FlashSalesPlanAppService :
IDistributedEventBus distributedEventBus,
FlashSalesPlanManager flashSalesPlanManager,
IFlashSalesResultRepository flashSalesResultRepository,
IAbpDistributedLock distributedLock)
IAbpDistributedLock distributedLock,
IFlashSalesPlanHasher flashSalesPlanHasher)
: base(flashSalesPlanRepository)
{
FlashSalesPlanRepository = flashSalesPlanRepository;
@ -68,6 +75,7 @@ public class FlashSalesPlanAppService :
FlashSalesPlanManager = flashSalesPlanManager;
FlashSalesResultRepository = flashSalesResultRepository;
DistributedLock = distributedLock;
FlashSalesPlanHasher = flashSalesPlanHasher;
}
public override async Task<FlashSalesPlanDto> GetAsync(Guid id)
@ -86,7 +94,7 @@ public class FlashSalesPlanAppService :
public override async Task<FlashSalesPlanDto> CreateAsync(FlashSalesPlanCreateDto input)
{
await CheckCreatePolicyAsync();
await AuthorizationService.CheckMultiStorePolicyAsync(input.StoreId, CreatePolicyName);
var flashSalesPlan = await FlashSalesPlanManager.CreateAsync(
input.StoreId,
@ -104,7 +112,7 @@ public class FlashSalesPlanAppService :
public override async Task<FlashSalesPlanDto> UpdateAsync(Guid id, FlashSalesPlanUpdateDto input)
{
await CheckUpdatePolicyAsync();
await AuthorizationService.CheckMultiStorePolicyAsync(input.StoreId, UpdatePolicyName);
var flashSalesPlan = await GetEntityByIdAsync(id);
@ -124,6 +132,15 @@ public class FlashSalesPlanAppService :
return await MapToGetOutputDtoAsync(flashSalesPlan);
}
public override async Task DeleteAsync(Guid id)
{
var flashSalesPlan = await GetEntityByIdAsync(id);
await AuthorizationService.CheckMultiStorePolicyAsync(flashSalesPlan.StoreId, DeletePolicyName);
await FlashSalesPlanRepository.DeleteAsync(flashSalesPlan);
}
protected override async Task<IQueryable<FlashSalesPlan>> CreateFilteredQueryAsync(FlashSalesPlanGetListInput input)
{
if (!input.OnlyShowPublished)
@ -212,58 +229,44 @@ public class FlashSalesPlanAppService :
throw new BusinessException(FlashSalesErrorCodes.PreOrderExipred);
}
var product = await ProductAppService.GetAsync(plan.ProductId);
var productSku = product.GetSkuById(plan.ProductSkuId);
if (!await ComparekHashTokenAsync(cacheHashToken, plan, product, productSku))
{
throw new BusinessException(FlashSalesErrorCodes.PreOrderExipred);
}
await RemoveCacheHashTokenAsync(plan);
var userId = CurrentUser.GetId();
var result = await CreatePendingFlashSalesResultAsync(plan, userId);
var CreateFlashSalesOrderEto = await PrepareCreateFlashSalesOrderEtoAsync(plan, product, productSku, result, input, userId, now);
var flashSalesReduceInventoryEto = await PrepareFlashSalesReduceInventoryEtoAsync(plan, result.Id, input, userId, now, cacheHashToken);
await DistributedEventBus.PublishAsync(CreateFlashSalesOrderEto);
/*
* FlashSalesReduceInventoryEto(success) -> CreateFlashSalesOrderEto -> CreateFlashSalesOrderCompleteEto
* FlashSalesReduceInventoryEto(failed) -> CreateFlashSalesOrderCompleteEto
*/
await DistributedEventBus.PublishAsync(flashSalesReduceInventoryEto);
}
protected virtual async Task<CreateFlashSalesOrderEto> PrepareCreateFlashSalesOrderEtoAsync(
FlashSalesPlanDto plan,
ProductDto product,
ProductSkuDto productSku,
FlashSalesResult result,
protected virtual Task<FlashSalesReduceInventoryEto> PrepareFlashSalesReduceInventoryEtoAsync(
FlashSalesPlanCacheItem plan,
Guid resultId,
CreateOrderInput input,
Guid userId,
DateTime now)
DateTime now,
string hashToken)
{
FlashSalesProductDetailEto productDetail = null;
var productDetailId = productSku.ProductDetailId ?? product.ProductDetailId;
if (productDetailId.HasValue)
{
productDetail = ObjectMapper.Map<ProductDetailDto, FlashSalesProductDetailEto>(await ProductDetailAppService.GetAsync(productDetailId.Value));
}
var productEto = ObjectMapper.Map<ProductDto, FlashSalesProductEto>(product);
productEto.TenantId = CurrentTenant.Id;
var planEto = ObjectMapper.Map<FlashSalesPlanDto, FlashSalesPlanEto>(plan);
var planEto = ObjectMapper.Map<FlashSalesPlanCacheItem, FlashSalesPlanEto>(plan);
planEto.TenantId = CurrentTenant.Id;
var eto = new CreateFlashSalesOrderEto()
var eto = new FlashSalesReduceInventoryEto()
{
TenantId = CurrentTenant.Id,
PlanId = plan.Id,
UserId = userId,
PendingResultId = result.Id,
PendingResultId = resultId,
StoreId = plan.StoreId,
CreateTime = now,
CustomerRemark = input.CustomerRemark,
Quantity = 1,//should configure
Product = productEto,
ProductDetail = productDetail,
Plan = planEto
Plan = planEto,
HashToken = hashToken
};
foreach (var item in input.ExtraProperties)
@ -271,7 +274,7 @@ public class FlashSalesPlanAppService :
eto.ExtraProperties.Add(item.Key, item.Value);
}
return eto;
return Task.FromResult(eto);
}
protected virtual async Task<FlashSalesPlanCacheItem> GetFlashSalesPlanCacheAsync(Guid id)
@ -283,54 +286,44 @@ public class FlashSalesPlanAppService :
});
}
protected virtual Task<string> GetCacheKeyAsync(FlashSalesPlanDto plan)
protected virtual Task<string> GetCacheKeyAsync(FlashSalesPlanCacheItem plan)
{
return Task.FromResult($"eshopflashsales_{CurrentUser.Id}_{plan.ProductSkuId}");
}
protected virtual async Task<string> GetCacheHashTokenAsync(FlashSalesPlanDto plan)
protected virtual async Task<string> GetCacheHashTokenAsync(FlashSalesPlanCacheItem plan)
{
return await TokenDistributedCache.GetStringAsync(await GetCacheKeyAsync(plan));
}
protected virtual async Task RemoveCacheHashTokenAsync(FlashSalesPlanDto plan)
protected virtual async Task RemoveCacheHashTokenAsync(FlashSalesPlanCacheItem plan)
{
await TokenDistributedCache.RemoveAsync(await GetCacheKeyAsync(plan));
}
protected virtual async Task SetCacheHashTokenAsync(FlashSalesPlanDto plan, ProductDto product, ProductSkuDto productSku)
protected virtual async Task SetCacheHashTokenAsync(FlashSalesPlanCacheItem plan, ProductDto product, ProductSkuDto productSku)
{
await TokenDistributedCache.SetStringAsync(await GetCacheKeyAsync(plan), await GetHashTokenAsync(plan, product, productSku), new DistributedCacheEntryOptions()
var hashToken = await FlashSalesPlanHasher.HashAsync(plan.LastModificationTime, product.LastModificationTime, productSku.LastModificationTime);
await TokenDistributedCache.SetStringAsync(await GetCacheKeyAsync(plan), hashToken, new DistributedCacheEntryOptions()
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(3)
});
}
protected virtual Task<string> GetHashTokenAsync(FlashSalesPlanDto plan, ProductDto product, ProductSkuDto productSku)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.UTF8.GetBytes($"{plan.LastModificationTime}|{product.LastModificationTime}|{productSku.LastModificationTime}");
var sb = new StringBuilder();
foreach (var t in md5.ComputeHash(inputBytes))
{
sb.Append(t.ToString("X2"));
}
return Task.FromResult(sb.ToString());
}
protected virtual async Task<bool> ComparekHashTokenAsync(string cacheHashToken, FlashSalesPlanDto plan, ProductDto product, ProductSkuDto productSku)
protected virtual async Task<bool> ComparekHashTokenAsync(string cacheHashToken, FlashSalesPlanCacheItem plan, ProductDto product, ProductSkuDto productSku)
{
if (cacheHashToken.IsNullOrWhiteSpace())
{
return false;
}
var hashToken = await GetHashTokenAsync(plan, product, productSku);
var hashToken = await FlashSalesPlanHasher.HashAsync(plan.LastModificationTime, product.LastModificationTime, productSku.LastModificationTime);
return cacheHashToken == hashToken;
}
protected virtual async Task<FlashSalesResult> CreatePendingFlashSalesResultAsync(FlashSalesPlanDto plan, Guid userId)
protected virtual async Task<FlashSalesResult> CreatePendingFlashSalesResultAsync(FlashSalesPlanCacheItem plan, Guid userId)
{
var lockKey = $"create-flash-sales-order-{plan.Id}-{userId}";
@ -342,7 +335,9 @@ public class FlashSalesPlanAppService :
}
// Prevent repeat submit
if (await FlashSalesResultRepository.AnyAsync(x => x.PlanId == plan.Id && x.UserId == userId))
if (await FlashSalesResultRepository.AnyAsync(x =>
x.PlanId == plan.Id && x.UserId == userId && (x.Status == FlashSalesResultStatus.Successful || x.Status == FlashSalesResultStatus.Pending))
)
{
throw new BusinessException(FlashSalesErrorCodes.AlreadySubmitCreateFlashSalesOrder);
}

9
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanCacheInvalidator.cs

@ -1,16 +1,21 @@
using System;
using System.Threading.Tasks;
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus;
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans;
public class FlashSalesPlanCacheInvalidator : ILocalEventHandler<EntityChangedEventData<FlashSalesPlan>>
public class FlashSalesPlanCacheInvalidator : ILocalEventHandler<EntityChangedEventData<FlashSalesPlan>>, ITransientDependency
{
protected IDistributedCache<FlashSalesPlanCacheItem, Guid> DistributedCache { get; }
public FlashSalesPlanCacheInvalidator(IDistributedCache<FlashSalesPlanCacheItem, Guid> distributedCache)
{
DistributedCache = distributedCache;
}
public virtual async Task HandleEventAsync(EntityChangedEventData<FlashSalesPlan> eventData)
{
await DistributedCache.RemoveAsync(eventData.Entity.Id);

1
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanCacheItem.cs

@ -6,4 +6,5 @@ namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans;
[Serializable]
public class FlashSalesPlanCacheItem : FlashSalesPlanDto
{
public Guid? TenantId { get; set; }
}

1
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/CreateFlashSalesOrderCompleteEto.cs

@ -1,5 +1,4 @@
using System;
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults;
using Volo.Abp.MultiTenancy;
using Volo.Abp.ObjectExtending;

1
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanEto.cs

@ -4,6 +4,7 @@ using Volo.Abp.ObjectExtending;
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans;
[Serializable]
public class FlashSalesPlanEto : FullAuditedEntityEto<Guid>, IMultiTenant
{
public Guid? TenantId { get; set; }

31
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanHasher.cs

@ -0,0 +1,31 @@
using System;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans;
public class FlashSalesPlanHasher : IFlashSalesPlanHasher, ITransientDependency
{
public virtual Task<string> HashAsync(DateTime? planLastModificationTime, DateTime? productLastModificationTime, DateTime? productSkuLastModificationTime)
{
var input = $"{planLastModificationTime}|{productLastModificationTime}|{productSkuLastModificationTime}";
return Task.FromResult(CreateMd5(input));
}
private static string CreateMd5(string input)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.UTF8.GetBytes(input);
var sb = new StringBuilder();
foreach (var t in md5.ComputeHash(inputBytes))
{
sb.Append(t.ToString("X2"));
}
return sb.ToString();
}
}

1
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductAttributeEto.cs

@ -4,6 +4,7 @@ using EasyAbp.EShop.Products.Products;
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans;
[Serializable]
public class FlashSalesProductAttributeEto : FullAuditedEntityEto<Guid>, IProductAttribute
{
public string DisplayName { get; set; }

1
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductAttributeOptionEto.cs

@ -3,6 +3,7 @@ using EasyAbp.EShop.Products.Products;
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans;
[Serializable]
public class FlashSalesProductAttributeOptionEto : FullAuditedEntityEto<Guid>, IProductAttributeOption
{
public string DisplayName { get; set; }

5
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductEto.cs

@ -5,6 +5,7 @@ using Volo.Abp.MultiTenancy;
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans;
[Serializable]
public class FlashSalesProductEto : FullAuditedEntityEto<Guid>, IProduct, IMultiTenant
{
public Guid? TenantId { get; set; }
@ -13,6 +14,8 @@ public class FlashSalesProductEto : FullAuditedEntityEto<Guid>, IProduct, IMulti
public string ProductGroupName { get; set; }
public string ProductGroupDisplayName { get; set; }
public Guid? ProductDetailId { get; set; }
public string UniqueName { get; set; }
@ -33,6 +36,8 @@ public class FlashSalesProductEto : FullAuditedEntityEto<Guid>, IProduct, IMulti
public bool IsHidden { get; set; }
public TimeSpan? PaymentExpireIn { get; set; }
public List<FlashSalesProductAttributeEto> ProductAttributes { get; set; }
public List<FlashSalesProductSkuEto> ProductSkus { get; set; }

6
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductSkuEto.cs

@ -1,10 +1,14 @@
using System;
using System.Collections.Generic;
using EasyAbp.EShop.Products.Products;
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans;
[Serializable]
public class FlashSalesProductSkuEto : FullAuditedEntityEto<Guid>, IProductSku
{
public List<Guid> AttributeOptionIds { get; set; }
public string SerializedAttributeOptionIds { get; set; }
public string Name { get; set; }
@ -19,6 +23,8 @@ public class FlashSalesProductSkuEto : FullAuditedEntityEto<Guid>, IProductSku
public int OrderMaxQuantity { get; set; }
public TimeSpan? PaymentExpireIn { get; set; }
public string MediaResources { get; set; }
public Guid? ProductDetailId { get; set; }

29
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesReduceInventoryEto.cs

@ -0,0 +1,29 @@
using System;
using Volo.Abp.MultiTenancy;
using Volo.Abp.ObjectExtending;
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans;
[Serializable]
public class FlashSalesReduceInventoryEto : ExtensibleObject, IMultiTenant
{
public Guid? TenantId { get; set; }
public Guid StoreId { get; set; }
public Guid PlanId { get; set; }
public Guid UserId { get; set; }
public Guid PendingResultId { get; set; }
public DateTime CreateTime { get; set; }
public string CustomerRemark { get; set; }
public int Quantity { get; set; }
public FlashSalesPlanEto Plan { get; set; }
public string HashToken { get; set; }
}

9
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/IFlashSalesPlanHasher.cs

@ -0,0 +1,9 @@
using System;
using System.Threading.Tasks;
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans;
public interface IFlashSalesPlanHasher
{
Task<string> HashAsync(DateTime? planLastModificationTime, DateTime? productLastModificationTime, DateTime? productSkuLastModificationTime);
}

6
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/ProductDetailEto.cs

@ -1,9 +1,13 @@
using System;
using Volo.Abp.MultiTenancy;
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans;
public class FlashSalesProductDetailEto : FullAuditedEntityEto<Guid>
[Serializable]
public class FlashSalesProductDetailEto : FullAuditedEntityEto<Guid>, IMultiTenant
{
public Guid? TenantId { get; set; }
public Guid? StoreId { get; set; }
public string Description { get; set; }

8
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesResults/FlashSalesResultFailedReason.cs

@ -0,0 +1,8 @@
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults;
public static class FlashSalesResultFailedReason
{
public const string InsufficientInventory = nameof(InsufficientInventory);
public const string PreOrderExipred = nameof(PreOrderExipred);
}

2
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesDomainModule.cs

@ -20,7 +20,7 @@ public class EShopPluginsFlashSalesDomainModule : AbpModule
Configure<AbpAutoMapperOptions>(options =>
{
options.AddProfile<EShopPluginsFlashSalesDomainAutoMapperProfile>(validate: true);
options.AddMaps<EShopPluginsFlashSalesDomainModule>(validate: true);
});
Configure<AbpDistributedEntityEventOptions>(options =>

2
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlan/ViewModels/EditFlashSalesPlanViewModel.cs

@ -8,8 +8,6 @@ namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.Fl
public class EditFlashSalesPlanViewModel : IHasConcurrencyStamp
{
[DisabledInput]
[ReadOnlyInput]
[Display(Name = "FlashSalesPlanStoreId")]
public Guid StoreId { get; set; }

15
plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp.EShop.Products.Plugins.FlashSales.Application.csproj

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\modules\EasyAbp.EShop.Products\src\EasyAbp.EShop.Products.Application\EasyAbp.EShop.Products.Application.csproj" />
<ProjectReference Include="..\EasyAbp.EShop.Plugins.FlashSales.Application.Contracts\EasyAbp.EShop.Plugins.FlashSales.Application.Contracts.csproj" />
</ItemGroup>
</Project>

37
plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesApplicationAutoMapperProfile.cs

@ -0,0 +1,37 @@
using System.Linq;
using AutoMapper;
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans;
using EasyAbp.EShop.Products.Options;
using EasyAbp.EShop.Products.ProductDetails;
using EasyAbp.EShop.Products.Products;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace EasyAbp.EShop.Plugins.FlashSales;
public class EShopProductsPluginsFlashSalesApplicationAutoMapperProfile : Profile, ISingletonDependency
{
public EShopProductsPluginsFlashSalesApplicationAutoMapperProfile(
IAttributeOptionIdsSerializer attributeOptionIdsSerializer,
IOptionsMonitor<EShopProductsOptions> options)
{
CreateMap<Product, FlashSalesProductEto>()
.ForMember(x => x.ProductGroupDisplayName, opt => opt.Ignore())
.AfterMap((src, dest) =>
{
var dict = options.CurrentValue.Groups.GetConfigurationsDictionary();
dest.ProductGroupDisplayName = dict[src.ProductGroupName].DisplayName;
})
.MapExtraProperties();
CreateMap<ProductSku, FlashSalesProductSkuEto>()
.ForMember(x => x.AttributeOptionIds, opt => opt.Ignore())
.AfterMap(async (src, dest) => dest.AttributeOptionIds = (await attributeOptionIdsSerializer.DeserializeAsync(src.SerializedAttributeOptionIds)).ToList())
.MapExtraProperties();
CreateMap<ProductAttribute, FlashSalesProductAttributeEto>()
.MapExtraProperties();
CreateMap<ProductAttributeOption, FlashSalesProductAttributeOptionEto>()
.MapExtraProperties();
CreateMap<ProductDetail, FlashSalesProductDetailEto>()
.MapExtraProperties();
}
}

28
plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesApplicationModule.cs

@ -0,0 +1,28 @@
using EasyAbp.EShop.Plugins.FlashSales;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AutoMapper;
using Volo.Abp.Modularity;
namespace EasyAbp.EShop.Products.Plugins.FlashSales;
[DependsOn(
typeof(EShopProductsApplicationModule),
typeof(EShopPluginsFlashSalesApplicationContractsModule)
)]
public class EShopProductsPluginsFlashSalesApplicationModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAutoMapperObjectMapper<EShopProductsPluginsFlashSalesApplicationModule>();
Configure<AbpAutoMapperOptions>(options =>
{
options.Configurators.Add(abpAutoMapperConfigurationContext =>
{
var profile = abpAutoMapperConfigurationContext.ServiceProvider
.GetRequiredService<EShopProductsPluginsFlashSalesApplicationAutoMapperProfile>();
abpAutoMapperConfigurationContext.MapperConfiguration.AddProfile(profile);
});
});
}
}

113
plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/FlashSalesReduceInventoryEventHandler.cs

@ -0,0 +1,113 @@
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans;
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults;
using EasyAbp.EShop.Products.ProductDetails;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.ObjectMapping;
using Volo.Abp.Uow;
namespace EasyAbp.EShop.Products.Products;
public class FlashSalesReduceInventoryEventHandler : IDistributedEventHandler<FlashSalesReduceInventoryEto>, ITransientDependency
{
protected IProductRepository ProductRepository { get; }
protected IProductDetailRepository ProductDetailRepository { get; }
protected ProductManager ProductManager { get; }
protected IDistributedEventBus DistributedEventBus { get; }
protected IObjectMapper ObjectMapper { get; }
protected IFlashSalesPlanHasher FlashSalesPlanHasher { get; }
public FlashSalesReduceInventoryEventHandler(
IProductRepository productRepository,
IProductDetailRepository productDetailRepository,
ProductManager productManager,
IDistributedEventBus distributedEventBus,
IObjectMapper objectMapper,
IFlashSalesPlanHasher flashSalesPlanHasher)
{
ProductRepository = productRepository;
ProductDetailRepository = productDetailRepository;
ProductManager = productManager;
DistributedEventBus = distributedEventBus;
ObjectMapper = objectMapper;
FlashSalesPlanHasher = flashSalesPlanHasher;
}
[UnitOfWork]
public virtual async Task HandleEventAsync(FlashSalesReduceInventoryEto eventData)
{
var product = await ProductRepository.GetAsync(eventData.Plan.ProductId);
var productSku = product.ProductSkus.Single(x => x.Id == eventData.Plan.ProductSkuId);
var hashToken = await FlashSalesPlanHasher.HashAsync(eventData.Plan.LastModificationTime, product.LastModificationTime, productSku.LastModificationTime);
if (hashToken != eventData.HashToken)
{
await DistributedEventBus.PublishAsync(new CreateFlashSalesOrderCompleteEto()
{
TenantId = eventData.TenantId,
PlanId = eventData.PlanId,
OrderId = null,
UserId = eventData.UserId,
StoreId = eventData.StoreId,
PendingResultId = eventData.PendingResultId,
Success = false,
Reason = FlashSalesResultFailedReason.PreOrderExipred
});
return;
}
if (!await ProductManager.TryReduceInventoryAsync(product, productSku, eventData.Quantity, true))
{
await DistributedEventBus.PublishAsync(new CreateFlashSalesOrderCompleteEto()
{
TenantId = eventData.TenantId,
PlanId = eventData.PlanId,
OrderId = null,
UserId = eventData.UserId,
StoreId = eventData.StoreId,
PendingResultId = eventData.PendingResultId,
Success = false,
Reason = FlashSalesResultFailedReason.InsufficientInventory
});
return;
}
var productDetailId = productSku.ProductDetailId ?? product.ProductDetailId;
var productDetailEto = productDetailId.HasValue ?
ObjectMapper.Map<ProductDetail, FlashSalesProductDetailEto>(await ProductDetailRepository.GetAsync(productDetailId.Value)) :
null;
var productEto = ObjectMapper.Map<Product, FlashSalesProductEto>(product);
var createFlashSalesOrderEto = new CreateFlashSalesOrderEto()
{
TenantId = eventData.TenantId,
PlanId = eventData.PlanId,
UserId = eventData.UserId,
PendingResultId = eventData.PendingResultId,
StoreId = eventData.StoreId,
CreateTime = eventData.CreateTime,
CustomerRemark = eventData.CustomerRemark,
Quantity = eventData.Quantity,
Product = productEto,
ProductDetail = productDetailEto,
Plan = eventData.Plan
};
foreach (var item in eventData.ExtraProperties)
{
createFlashSalesOrderEto.ExtraProperties.Add(item.Key, item.Value);
}
await DistributedEventBus.PublishAsync(createFlashSalesOrderEto);
}
}

3
plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait />
</Weavers>

30
plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

1
samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSample.Application.csproj

@ -14,6 +14,7 @@
<ProjectReference Include="..\..\..\..\..\plugins\Coupons\src\EasyAbp.EShop.Orders.Plugins.Coupons\EasyAbp.EShop.Orders.Plugins.Coupons.csproj" />
<ProjectReference Include="..\..\..\..\..\plugins\FlashSales\src\EasyAbp.EShop.Orders.Plugins.FlashSales.Application\EasyAbp.EShop.Orders.Plugins.FlashSales.Application.csproj" />
<ProjectReference Include="..\..\..\..\..\plugins\FlashSales\src\EasyAbp.EShop.Plugins.FlashSales.Application\EasyAbp.EShop.Plugins.FlashSales.Application.csproj" />
<ProjectReference Include="..\..\..\..\..\plugins\FlashSales\src\EasyAbp.EShop.Products.Plugins.FlashSales.Application\EasyAbp.EShop.Products.Plugins.FlashSales.Application.csproj" />
<ProjectReference Include="..\EShopSample.Domain\EShopSample.Domain.csproj" />
<ProjectReference Include="..\EShopSample.Application.Contracts\EShopSample.Application.Contracts.csproj" />
</ItemGroup>

3
samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSampleApplicationModule.cs

@ -1,8 +1,10 @@
using EasyAbp.EShop;
using EasyAbp.EShop.Orders.Plugins.Coupons;
using EasyAbp.EShop.Orders.Plugins.FlashSales;
using EasyAbp.EShop.Plugins.Baskets;
using EasyAbp.EShop.Plugins.Coupons;
using EasyAbp.EShop.Plugins.FlashSales;
using EasyAbp.EShop.Products.Plugins.FlashSales;
using EasyAbp.PaymentService;
using EasyAbp.PaymentService.Prepayment;
using EasyAbp.PaymentService.WeChatPay;
@ -30,6 +32,7 @@ namespace EShopSample
typeof(EShopOrdersPluginsCouponsModule),
typeof(EShopPluginsFlashSalesApplicationModule),
typeof(EShopOrdersPluginsFlashSalesApplicationModule),
typeof(EShopProductsPluginsFlashSalesApplicationModule),
typeof(PaymentServiceApplicationModule),
typeof(PaymentServiceWeChatPayApplicationModule),
typeof(PaymentServicePrepaymentApplicationModule)

Loading…
Cancel
Save