mirror of https://github.com/EasyAbp/EShop.git
26 changed files with 4991 additions and 17 deletions
@ -0,0 +1,15 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\..\..\modules\EasyAbp.EShop.Orders\src\EasyAbp.EShop.Orders.Application\EasyAbp.EShop.Orders.Application.csproj" /> |
|||
<ProjectReference Include="..\EasyAbp.EShop.Plugins.Coupons.Domain.Shared\EasyAbp.EShop.Plugins.Coupons.Domain.Shared.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,101 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Orders.Orders; |
|||
using EasyAbp.EShop.Orders.Orders.Dtos; |
|||
using EasyAbp.EShop.Plugins.Coupons; |
|||
using EasyAbp.EShop.Plugins.Coupons.Coupons; |
|||
using EasyAbp.EShop.Plugins.Coupons.CouponTemplates; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.Timing; |
|||
|
|||
namespace EasyAbp.EShop.Orders.Plugins.Coupons.Authorization |
|||
{ |
|||
public class CouponOrderCreationAuthorizationHandler : OrderCreationAuthorizationHandler |
|||
{ |
|||
private readonly IClock _clock; |
|||
private readonly ICouponLookupService _couponLookupService; |
|||
private readonly ICouponTemplateLookupService _couponTemplateLookupService; |
|||
|
|||
public CouponOrderCreationAuthorizationHandler( |
|||
IClock clock, |
|||
ICouponLookupService couponLookupService, |
|||
ICouponTemplateLookupService couponTemplateLookupService) |
|||
{ |
|||
_clock = clock; |
|||
_couponLookupService = couponLookupService; |
|||
_couponTemplateLookupService = couponTemplateLookupService; |
|||
} |
|||
|
|||
protected override async Task HandleOrderCreationAsync(AuthorizationHandlerContext context, |
|||
OrderOperationAuthorizationRequirement requirement, OrderCreationResource resource) |
|||
{ |
|||
var couponId = resource.Input.GetProperty<Guid?>(CouponsConsts.OrderCouponIdPropertyName); |
|||
|
|||
if (!couponId.HasValue) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var now = _clock.Now; |
|||
|
|||
var coupon = await _couponLookupService.FindByIdAsync(couponId.Value); |
|||
|
|||
if (coupon == null || coupon.ExpirationTime < now) |
|||
{ |
|||
context.Fail(); |
|||
return; |
|||
} |
|||
|
|||
var couponTemplate = await _couponTemplateLookupService.FindByIdAsync(coupon.CouponTemplateId); |
|||
|
|||
if (couponTemplate == null || |
|||
!IsInUsableTime(couponTemplate, now) || |
|||
!IsOrderInScope(couponTemplate, resource)) |
|||
{ |
|||
context.Fail(); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
protected virtual bool IsOrderInScope(ICouponTemplate couponTemplate, OrderCreationResource resource) |
|||
{ |
|||
if (couponTemplate.IsUnscoped) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
var expectedOrderLines = new List<CreateOrderLineDto>(); |
|||
|
|||
foreach (var scope in couponTemplate.Scopes) |
|||
{ |
|||
expectedOrderLines.AddRange(resource.Input.OrderLines |
|||
.WhereIf(scope.ProductGroupName != null, |
|||
x => resource.ProductDictionary[x.ProductId].ProductGroupName == scope.ProductGroupName) |
|||
.WhereIf(scope.ProductId.HasValue, x => x.ProductId == scope.ProductId) |
|||
.WhereIf(scope.ProductSkuId.HasValue, x => x.ProductSkuId == scope.ProductSkuId)); |
|||
} |
|||
|
|||
if (couponTemplate.IsCrossProductAllowed) |
|||
{ |
|||
return expectedOrderLines.Sum(x => GetOrderLineProductPrice(x, resource) * x.Quantity) >= |
|||
couponTemplate.ConditionAmount; |
|||
} |
|||
|
|||
return expectedOrderLines.Any(orderLine => |
|||
GetOrderLineProductPrice(orderLine, resource) >= couponTemplate.ConditionAmount); |
|||
} |
|||
|
|||
protected virtual decimal GetOrderLineProductPrice(CreateOrderLineDto createOrderLineDto, OrderCreationResource resource) |
|||
{ |
|||
return resource.ProductDictionary[createOrderLineDto.ProductId].GetSkuById(createOrderLineDto.ProductSkuId).Price; |
|||
} |
|||
|
|||
protected virtual bool IsInUsableTime(ICouponTemplate couponTemplate, DateTime now) |
|||
{ |
|||
return couponTemplate.UsableBeginTime <= now && couponTemplate.UsableEndTime > now; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using EasyAbp.EShop.Orders.Plugins.Coupons.Authorization; |
|||
using EasyAbp.EShop.Plugins.Coupons; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace EasyAbp.EShop.Orders.Plugins.Coupons |
|||
{ |
|||
[DependsOn( |
|||
typeof(EShopOrdersDomainModule), |
|||
typeof(EShopPluginsCouponsDomainSharedModule) |
|||
)] |
|||
public class EShopOrdersPluginsCouponsModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddSingleton<IAuthorizationHandler, CouponOrderCreationAuthorizationHandler>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
using System; |
|||
using EasyAbp.EShop.Orders.Orders; |
|||
using EasyAbp.EShop.Orders.Orders.Dtos; |
|||
using EasyAbp.EShop.Plugins.Coupons; |
|||
using Volo.Abp.ObjectExtending; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace EasyAbp.EShop.Orders.Plugins.Coupons.ObjectExtending |
|||
{ |
|||
public static class EShopOrdersPluginsCouponsObjectExtensions |
|||
{ |
|||
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); |
|||
|
|||
public static void Configure() |
|||
{ |
|||
OneTimeRunner.Run(() => |
|||
{ |
|||
/* You can configure extension properties to entities or other object types |
|||
* defined in the depended modules. |
|||
* |
|||
* If you are using EF Core and want to map the entity extension properties to new |
|||
* table fields in the database, then configure them in the EShopSampleEfCoreEntityExtensionMappings |
|||
* |
|||
* Example: |
|||
* |
|||
* ObjectExtensionManager.Instance |
|||
* .AddOrUpdateProperty<IdentityRole, string>("Title"); |
|||
* |
|||
* See the documentation for more: |
|||
* https://docs.abp.io/en/abp/latest/Object-Extensions
|
|||
*/ |
|||
|
|||
ObjectExtensionManager.Instance |
|||
.AddOrUpdate( |
|||
new[] |
|||
{ |
|||
typeof(Order), |
|||
typeof(CreateOrderDto) |
|||
}, |
|||
config => |
|||
{ |
|||
config.AddOrUpdateProperty<Guid?>(CouponsConsts.OrderCouponIdPropertyName); |
|||
} |
|||
); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Orders.Orders; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EasyAbp.EShop.Orders.Plugins.Coupons.OrderDiscount |
|||
{ |
|||
public class CouponOrderDiscountProvider : IOrderDiscountProvider, ITransientDependency |
|||
{ |
|||
public async Task<Order> DiscountAsync(Order order, Dictionary<string, object> inputExtraProperties) |
|||
{ |
|||
throw new System.NotImplementedException(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.Coupons.CouponTemplates |
|||
{ |
|||
public interface ICouponTemplate |
|||
{ |
|||
Guid? TenantId { get; } |
|||
|
|||
Guid? StoreId { get; } |
|||
|
|||
CouponType CouponType { get; } |
|||
|
|||
[CanBeNull] |
|||
string UniqueName { get; } |
|||
|
|||
[NotNull] |
|||
string DisplayName { get; } |
|||
|
|||
[CanBeNull] |
|||
string Description { get; } |
|||
|
|||
TimeSpan? UsableDuration { get; } |
|||
|
|||
DateTime? UsableBeginTime { get; } |
|||
|
|||
DateTime? UsableEndTime { get; } |
|||
|
|||
decimal ConditionAmount { get; } |
|||
|
|||
decimal DiscountAmount { get; } |
|||
|
|||
bool IsCrossProductAllowed { get; } |
|||
|
|||
bool IsUnscoped { get; } |
|||
|
|||
IEnumerable<ICouponTemplateScope> Scopes { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.Coupons.CouponTemplates |
|||
{ |
|||
public interface ICouponTemplateLookupService |
|||
{ |
|||
Task<ICouponTemplate> FindByIdAsync(Guid id, CancellationToken cancellationToken = default); |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using System; |
|||
using EasyAbp.EShop.Stores.Stores; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.Coupons.CouponTemplates |
|||
{ |
|||
public interface ICouponTemplateScope : IMultiStore |
|||
{ |
|||
[CanBeNull] |
|||
string ProductGroupName { get; } |
|||
|
|||
Guid? ProductId { get; } |
|||
|
|||
Guid? ProductSkuId { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using System; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.Coupons.Coupons |
|||
{ |
|||
public interface ICoupon |
|||
{ |
|||
Guid CouponTemplateId { get; } |
|||
|
|||
Guid UserId { get; } |
|||
|
|||
Guid? OrderId { get; } |
|||
|
|||
DateTime? ExpirationTime { get; } |
|||
|
|||
DateTime? UsedTime { get; } |
|||
|
|||
decimal? DiscountedAmount { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.Coupons.Coupons |
|||
{ |
|||
public interface ICouponLookupService |
|||
{ |
|||
Task<ICoupon> FindByIdAsync(Guid id, CancellationToken cancellationToken = default); |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace EasyAbp.EShop.Plugins.Coupons |
|||
{ |
|||
public static class CouponsConsts |
|||
{ |
|||
public const string OrderCouponIdPropertyName = "CouponId"; |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,89 @@ |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
|
|||
namespace EShopSample.Migrations |
|||
{ |
|||
public partial class ConfiguredCouponTemplateScope : Migration |
|||
{ |
|||
protected override void Up(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.DropForeignKey( |
|||
name: "FK_CouponTemplateScope_EasyAbpEShopPluginsCouponsCouponTemplates_CouponTemplateId", |
|||
table: "CouponTemplateScope"); |
|||
|
|||
migrationBuilder.DropPrimaryKey( |
|||
name: "PK_CouponTemplateScope", |
|||
table: "CouponTemplateScope"); |
|||
|
|||
migrationBuilder.RenameTable( |
|||
name: "CouponTemplateScope", |
|||
newName: "EasyAbpEShopPluginsCouponsCouponTemplateScopes"); |
|||
|
|||
migrationBuilder.RenameIndex( |
|||
name: "IX_CouponTemplateScope_CouponTemplateId", |
|||
table: "EasyAbpEShopPluginsCouponsCouponTemplateScopes", |
|||
newName: "IX_EasyAbpEShopPluginsCouponsCouponTemplateScopes_CouponTemplateId"); |
|||
|
|||
migrationBuilder.AlterColumn<bool>( |
|||
name: "IsDeleted", |
|||
table: "EasyAbpEShopPluginsCouponsCouponTemplateScopes", |
|||
nullable: false, |
|||
defaultValue: false, |
|||
oldClrType: typeof(bool), |
|||
oldType: "bit"); |
|||
|
|||
migrationBuilder.AddPrimaryKey( |
|||
name: "PK_EasyAbpEShopPluginsCouponsCouponTemplateScopes", |
|||
table: "EasyAbpEShopPluginsCouponsCouponTemplateScopes", |
|||
column: "Id"); |
|||
|
|||
migrationBuilder.AddForeignKey( |
|||
name: "FK_EasyAbpEShopPluginsCouponsCouponTemplateScopes_EasyAbpEShopPluginsCouponsCouponTemplates_CouponTemplateId", |
|||
table: "EasyAbpEShopPluginsCouponsCouponTemplateScopes", |
|||
column: "CouponTemplateId", |
|||
principalTable: "EasyAbpEShopPluginsCouponsCouponTemplates", |
|||
principalColumn: "Id", |
|||
onDelete: ReferentialAction.Restrict); |
|||
} |
|||
|
|||
protected override void Down(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.DropForeignKey( |
|||
name: "FK_EasyAbpEShopPluginsCouponsCouponTemplateScopes_EasyAbpEShopPluginsCouponsCouponTemplates_CouponTemplateId", |
|||
table: "EasyAbpEShopPluginsCouponsCouponTemplateScopes"); |
|||
|
|||
migrationBuilder.DropPrimaryKey( |
|||
name: "PK_EasyAbpEShopPluginsCouponsCouponTemplateScopes", |
|||
table: "EasyAbpEShopPluginsCouponsCouponTemplateScopes"); |
|||
|
|||
migrationBuilder.RenameTable( |
|||
name: "EasyAbpEShopPluginsCouponsCouponTemplateScopes", |
|||
newName: "CouponTemplateScope"); |
|||
|
|||
migrationBuilder.RenameIndex( |
|||
name: "IX_EasyAbpEShopPluginsCouponsCouponTemplateScopes_CouponTemplateId", |
|||
table: "CouponTemplateScope", |
|||
newName: "IX_CouponTemplateScope_CouponTemplateId"); |
|||
|
|||
migrationBuilder.AlterColumn<bool>( |
|||
name: "IsDeleted", |
|||
table: "CouponTemplateScope", |
|||
type: "bit", |
|||
nullable: false, |
|||
oldClrType: typeof(bool), |
|||
oldDefaultValue: false); |
|||
|
|||
migrationBuilder.AddPrimaryKey( |
|||
name: "PK_CouponTemplateScope", |
|||
table: "CouponTemplateScope", |
|||
column: "Id"); |
|||
|
|||
migrationBuilder.AddForeignKey( |
|||
name: "FK_CouponTemplateScope_EasyAbpEShopPluginsCouponsCouponTemplates_CouponTemplateId", |
|||
table: "CouponTemplateScope", |
|||
column: "CouponTemplateId", |
|||
principalTable: "EasyAbpEShopPluginsCouponsCouponTemplates", |
|||
principalColumn: "Id", |
|||
onDelete: ReferentialAction.Restrict); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue