mirror of https://github.com/EasyAbp/EShop.git
92 changed files with 2524 additions and 73 deletions
@ -0,0 +1,223 @@ |
|||
# Remove the line below if you want to inherit .editorconfig settings from higher directories |
|||
root = true |
|||
|
|||
# C# files |
|||
[*.cs] |
|||
|
|||
#### Core EditorConfig Options #### |
|||
|
|||
# Indentation and spacing |
|||
indent_size = 4 |
|||
indent_style = space |
|||
tab_width = 4 |
|||
|
|||
# New line preferences |
|||
end_of_line = crlf |
|||
insert_final_newline = false |
|||
|
|||
#### .NET Coding Conventions #### |
|||
|
|||
# Organize usings |
|||
dotnet_separate_import_directive_groups = false |
|||
dotnet_sort_system_directives_first = true |
|||
file_header_template = unset |
|||
|
|||
# this. and Me. preferences |
|||
dotnet_style_qualification_for_event = false |
|||
dotnet_style_qualification_for_field = false |
|||
dotnet_style_qualification_for_method = false |
|||
dotnet_style_qualification_for_property = false |
|||
|
|||
# Language keywords vs BCL types preferences |
|||
dotnet_style_predefined_type_for_locals_parameters_members = true |
|||
dotnet_style_predefined_type_for_member_access = true |
|||
|
|||
# Parentheses preferences |
|||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity |
|||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity |
|||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary |
|||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity |
|||
|
|||
# Modifier preferences |
|||
dotnet_style_require_accessibility_modifiers = omit_if_default |
|||
|
|||
# Expression-level preferences |
|||
dotnet_style_coalesce_expression = true |
|||
dotnet_style_collection_initializer = true |
|||
dotnet_style_explicit_tuple_names = true |
|||
dotnet_style_namespace_match_folder = true |
|||
dotnet_style_null_propagation = true |
|||
dotnet_style_object_initializer = true |
|||
dotnet_style_operator_placement_when_wrapping = beginning_of_line |
|||
dotnet_style_prefer_auto_properties = true |
|||
dotnet_style_prefer_compound_assignment = true |
|||
dotnet_style_prefer_conditional_expression_over_assignment = true |
|||
dotnet_style_prefer_conditional_expression_over_return = true |
|||
dotnet_style_prefer_inferred_anonymous_type_member_names = true |
|||
dotnet_style_prefer_inferred_tuple_names = true |
|||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true |
|||
dotnet_style_prefer_simplified_boolean_expressions = true |
|||
dotnet_style_prefer_simplified_interpolation = true |
|||
|
|||
# Field preferences |
|||
dotnet_style_readonly_field = true |
|||
|
|||
# Parameter preferences |
|||
dotnet_code_quality_unused_parameters = all |
|||
|
|||
# Suppression preferences |
|||
dotnet_remove_unnecessary_suppression_exclusions = 0 |
|||
|
|||
# New line preferences |
|||
dotnet_style_allow_multiple_blank_lines_experimental = false |
|||
dotnet_style_allow_statement_immediately_after_block_experimental = false |
|||
|
|||
#### C# Coding Conventions #### |
|||
|
|||
# var preferences |
|||
csharp_style_var_elsewhere = false |
|||
csharp_style_var_for_built_in_types = false |
|||
csharp_style_var_when_type_is_apparent = false |
|||
|
|||
# Expression-bodied members |
|||
csharp_style_expression_bodied_accessors = true |
|||
csharp_style_expression_bodied_constructors = false |
|||
csharp_style_expression_bodied_indexers = true |
|||
csharp_style_expression_bodied_lambdas = true |
|||
csharp_style_expression_bodied_local_functions = false |
|||
csharp_style_expression_bodied_methods = false |
|||
csharp_style_expression_bodied_operators = false |
|||
csharp_style_expression_bodied_properties = true |
|||
|
|||
# Pattern matching preferences |
|||
csharp_style_pattern_matching_over_as_with_null_check = true |
|||
csharp_style_pattern_matching_over_is_with_cast_check = true |
|||
csharp_style_prefer_extended_property_pattern = true |
|||
csharp_style_prefer_not_pattern = true |
|||
csharp_style_prefer_pattern_matching = true |
|||
csharp_style_prefer_switch_expression = true |
|||
|
|||
# Null-checking preferences |
|||
csharp_style_conditional_delegate_call = true |
|||
csharp_style_prefer_parameter_null_checking = true |
|||
|
|||
# Modifier preferences |
|||
csharp_prefer_static_local_function = true |
|||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async |
|||
|
|||
# Code-block preferences |
|||
csharp_prefer_braces = true |
|||
csharp_prefer_simple_using_statement = true |
|||
csharp_style_namespace_declarations = file_scoped |
|||
csharp_style_prefer_method_group_conversion = true |
|||
|
|||
# Expression-level preferences |
|||
csharp_prefer_simple_default_expression = true |
|||
csharp_style_deconstructed_variable_declaration = true |
|||
csharp_style_implicit_object_creation_when_type_is_apparent = true |
|||
csharp_style_inlined_variable_declaration = true |
|||
csharp_style_prefer_index_operator = true |
|||
csharp_style_prefer_local_over_anonymous_function = true |
|||
csharp_style_prefer_null_check_over_type_check = true |
|||
csharp_style_prefer_range_operator = true |
|||
csharp_style_prefer_tuple_swap = true |
|||
csharp_style_throw_expression = true |
|||
csharp_style_unused_value_assignment_preference = discard_variable |
|||
csharp_style_unused_value_expression_statement_preference = discard_variable |
|||
|
|||
# 'using' directive preferences |
|||
csharp_using_directive_placement = outside_namespace:suggestion |
|||
|
|||
# New line preferences |
|||
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true |
|||
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false |
|||
csharp_style_allow_embedded_statements_on_same_line_experimental = true |
|||
|
|||
#### C# Formatting Rules #### |
|||
|
|||
# New line preferences |
|||
csharp_new_line_before_catch = true |
|||
csharp_new_line_before_else = true |
|||
csharp_new_line_before_finally = true |
|||
csharp_new_line_before_members_in_anonymous_types = true |
|||
csharp_new_line_before_members_in_object_initializers = true |
|||
csharp_new_line_before_open_brace = all |
|||
csharp_new_line_between_query_expression_clauses = true |
|||
|
|||
# Indentation preferences |
|||
csharp_indent_block_contents = true |
|||
csharp_indent_braces = false |
|||
csharp_indent_case_contents = true |
|||
csharp_indent_case_contents_when_block = true |
|||
csharp_indent_labels = one_less_than_current |
|||
csharp_indent_switch_labels = true |
|||
|
|||
# Space preferences |
|||
csharp_space_after_cast = false |
|||
csharp_space_after_colon_in_inheritance_clause = true |
|||
csharp_space_after_comma = true |
|||
csharp_space_after_dot = false |
|||
csharp_space_after_keywords_in_control_flow_statements = true |
|||
csharp_space_after_semicolon_in_for_statement = true |
|||
csharp_space_around_binary_operators = before_and_after |
|||
csharp_space_around_declaration_statements = false |
|||
csharp_space_before_colon_in_inheritance_clause = true |
|||
csharp_space_before_comma = false |
|||
csharp_space_before_dot = false |
|||
csharp_space_before_open_square_brackets = false |
|||
csharp_space_before_semicolon_in_for_statement = false |
|||
csharp_space_between_empty_square_brackets = false |
|||
csharp_space_between_method_call_empty_parameter_list_parentheses = false |
|||
csharp_space_between_method_call_name_and_opening_parenthesis = false |
|||
csharp_space_between_method_call_parameter_list_parentheses = false |
|||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false |
|||
csharp_space_between_method_declaration_name_and_open_parenthesis = false |
|||
csharp_space_between_method_declaration_parameter_list_parentheses = false |
|||
csharp_space_between_parentheses = false |
|||
csharp_space_between_square_brackets = false |
|||
|
|||
# Wrapping preferences |
|||
csharp_preserve_single_line_blocks = true |
|||
csharp_preserve_single_line_statements = true |
|||
|
|||
#### Naming styles #### |
|||
|
|||
# Naming rules |
|||
|
|||
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion |
|||
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface |
|||
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i |
|||
|
|||
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion |
|||
dotnet_naming_rule.types_should_be_pascal_case.symbols = types |
|||
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case |
|||
|
|||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion |
|||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members |
|||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case |
|||
|
|||
# Symbol specifications |
|||
|
|||
dotnet_naming_symbols.interface.applicable_kinds = interface |
|||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected |
|||
dotnet_naming_symbols.interface.required_modifiers = |
|||
|
|||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum |
|||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected |
|||
dotnet_naming_symbols.types.required_modifiers = |
|||
|
|||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method |
|||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected |
|||
dotnet_naming_symbols.non_field_members.required_modifiers = |
|||
|
|||
# Naming styles |
|||
|
|||
dotnet_naming_style.pascal_case.required_prefix = |
|||
dotnet_naming_style.pascal_case.required_suffix = |
|||
dotnet_naming_style.pascal_case.word_separator = |
|||
dotnet_naming_style.pascal_case.capitalization = pascal_case |
|||
|
|||
dotnet_naming_style.begins_with_i.required_prefix = I |
|||
dotnet_naming_style.begins_with_i.required_suffix = |
|||
dotnet_naming_style.begins_with_i.word_separator = |
|||
dotnet_naming_style.begins_with_i.capitalization = pascal_case |
|||
@ -0,0 +1,95 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Orders.Orders.Dtos; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
using EasyAbp.EShop.Products.ProductDetails.Dtos; |
|||
using EasyAbp.EShop.Products.Products.Dtos; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
using Volo.Abp.ObjectMapping; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace EasyAbp.EShop.Orders.Orders; |
|||
|
|||
public class CreateFlashSalesOrderEventHandler : IDistributedEventHandler<CreateFlashSalesOrderEto>, ITransientDependency |
|||
{ |
|||
protected INewOrderGenerator NewOrderGenerator { get; } |
|||
|
|||
protected IObjectMapper ObjectMapper { get; } |
|||
|
|||
protected IEnumerable<IOrderDiscountProvider> OrderDiscountProviders { get; } |
|||
|
|||
protected IOrderRepository OrderRepository { get; } |
|||
|
|||
protected IDistributedEventBus DistributedEventBus { get; } |
|||
|
|||
public CreateFlashSalesOrderEventHandler( |
|||
INewOrderGenerator newOrderGenerator, |
|||
IObjectMapper objectMapper, |
|||
IEnumerable<IOrderDiscountProvider> orderDiscountProviders, |
|||
IOrderRepository orderRepository, |
|||
IDistributedEventBus distributedEventBus) |
|||
{ |
|||
NewOrderGenerator = newOrderGenerator; |
|||
ObjectMapper = objectMapper; |
|||
OrderDiscountProviders = orderDiscountProviders; |
|||
OrderRepository = orderRepository; |
|||
DistributedEventBus = distributedEventBus; |
|||
} |
|||
|
|||
[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, |
|||
CustomerRemark = eventData.CustomerRemark, |
|||
OrderLines = new List<CreateOrderLineDto>() |
|||
{ |
|||
new CreateOrderLineDto() |
|||
{ |
|||
ProductId = eventData.Plan.ProductId, |
|||
ProductSkuId = eventData.Plan.ProductSkuId, |
|||
Quantity = eventData.Quantity |
|||
} |
|||
} |
|||
}; |
|||
var productDict = new Dictionary<Guid, ProductDto>() |
|||
{ |
|||
{eventData.Product.Id, ObjectMapper.Map<FlashSalesProductEto,ProductDto>(eventData.Product)} |
|||
}; |
|||
var productDetailDict = new Dictionary<Guid, ProductDetailDto>() |
|||
{ |
|||
{eventData.ProductDetail.Id, ObjectMapper.Map<FlashSalesProductDetailEto,ProductDetailDto>(eventData.ProductDetail)} |
|||
}; |
|||
|
|||
var order = await NewOrderGenerator.GenerateAsync(eventData.UserId, input, productDict, productDetailDict); |
|||
|
|||
await DiscountOrderAsync(order, productDict); |
|||
|
|||
await OrderRepository.InsertAsync(order, autoSave: true); |
|||
|
|||
await DistributedEventBus.PublishAsync(new CreateFlashSalesOrderCompleteEto() |
|||
{ |
|||
TenantId = eventData.TenantId, |
|||
PlanId = eventData.PlanId, |
|||
OrderId = order.Id, |
|||
UserId = eventData.UserId, |
|||
StoreId = eventData.StoreId, |
|||
PendingResultId = eventData.PendingResultId, |
|||
Success = true |
|||
}); |
|||
} |
|||
|
|||
protected virtual async Task DiscountOrderAsync(Order order, Dictionary<Guid, ProductDto> productDict) |
|||
{ |
|||
foreach (var provider in OrderDiscountProviders) |
|||
{ |
|||
await provider.DiscountAsync(order, productDict); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EasyAbp.EShop.Orders.Orders; |
|||
|
|||
public class FlashSalesOrderCreationAuthorizationHandler : OrderCreationAuthorizationHandler |
|||
{ |
|||
protected IAbpLazyServiceProvider LazyServiceProvider { get; } |
|||
|
|||
public FlashSalesOrderCreationAuthorizationHandler(IAbpLazyServiceProvider lazyServiceProvider) |
|||
{ |
|||
LazyServiceProvider = lazyServiceProvider; |
|||
} |
|||
|
|||
protected override async Task HandleOrderCreationAsync(AuthorizationHandlerContext context, |
|||
OrderOperationAuthorizationRequirement requirement, OrderCreationResource resource) |
|||
{ |
|||
var flashSalesPlanAppService = LazyServiceProvider.LazyGetRequiredService<IFlashSalesPlanAppService>(); |
|||
foreach (var orderLine in resource.Input.OrderLines) |
|||
{ |
|||
var plans = await flashSalesPlanAppService.GetListAsync(new FlashSalesPlanGetListInput() |
|||
{ |
|||
StoreId = resource.Input.StoreId, |
|||
ProductId = orderLine.ProductId, |
|||
ProductSkuId = orderLine.ProductSkuId, |
|||
OnlyShowPublished = true |
|||
}); |
|||
if (plans.Items.Count > 0) |
|||
{ |
|||
context.Fail(); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
context.Succeed(requirement); |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
using System.Collections.Generic; |
|||
using System; |
|||
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.DependencyInjection; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace EasyAbp.EShop.Orders |
|||
{ |
|||
public class EShopOrdersPluginsFlashSalesApplicationAutoMapperProfile : Profile, ISingletonDependency |
|||
{ |
|||
public EShopOrdersPluginsFlashSalesApplicationAutoMapperProfile(IJsonSerializer jsonSerializer) |
|||
{ |
|||
/* 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()) |
|||
.MapExtraProperties(); |
|||
CreateMap<FlashSalesProductSkuEto, ProductSkuDto>(MemberList.Source) |
|||
.ForMember(x => x.AttributeOptionIds, opt => opt.Ignore()) |
|||
.AfterMap((src, dest) => dest.AttributeOptionIds = jsonSerializer.Deserialize<List<Guid>>(src.SerializedAttributeOptionIds)) |
|||
.MapExtraProperties(); |
|||
CreateMap<FlashSalesProductAttributeEto, ProductAttributeDto>(MemberList.Source) |
|||
.MapExtraProperties(); |
|||
CreateMap<FlashSalesProductAttributeOptionEto, ProductAttributeOptionDto>(MemberList.Source) |
|||
.MapExtraProperties(); |
|||
CreateMap<FlashSalesProductDetailEto, ProductDetailDto>(MemberList.Source) |
|||
.MapExtraProperties(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,12 +1,37 @@ |
|||
using EasyAbp.EShop.Orders; |
|||
using EasyAbp.EShop.Orders.Orders; |
|||
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; |
|||
|
|||
[DependsOn( |
|||
typeof(EShopOrdersApplicationModule), |
|||
typeof(EShopProductsApplicationContractsModule), |
|||
typeof(EShopPluginsFlashSalesApplicationContractsModule) |
|||
)] |
|||
public class EShopOrdersPluginsFlashSalesApplicationModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddSingleton<IAuthorizationHandler, FlashSalesOrderCreationAuthorizationHandler>(); |
|||
} |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddAutoMapperObjectMapper<EShopOrdersPluginsFlashSalesApplicationAutoMapperProfile>(); |
|||
Configure<AbpAutoMapperOptions>(options => |
|||
{ |
|||
options.Configurators.Add(abpAutoMapperConfigurationContext => |
|||
{ |
|||
var profile = abpAutoMapperConfigurationContext.ServiceProvider |
|||
.GetRequiredService<EShopOrdersPluginsFlashSalesApplicationAutoMapperProfile>(); |
|||
|
|||
abpAutoMapperConfigurationContext.MapperConfiguration.AddProfile(profile); |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,9 @@ |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos; |
|||
|
|||
public class CreateOrderInput : ExtensibleEntityDto |
|||
{ |
|||
public string CustomerRemark { get; set; } |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Domain.Entities; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos; |
|||
|
|||
[Serializable] |
|||
public class FlashSalesPlanCreateDto : ExtensibleEntityDto |
|||
{ |
|||
public Guid StoreId { get; set; } |
|||
|
|||
public DateTime BeginTime { get; set; } |
|||
|
|||
public DateTime EndTime { get; set; } |
|||
|
|||
public Guid ProductId { get; set; } |
|||
|
|||
public Guid ProductSkuId { get; set; } |
|||
|
|||
public bool IsPublished { get; set; } |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Domain.Entities; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos; |
|||
|
|||
[Serializable] |
|||
public class FlashSalesPlanDto : ExtensibleFullAuditedEntityDto<Guid>, IHasConcurrencyStamp |
|||
{ |
|||
public Guid StoreId { get; set; } |
|||
|
|||
public DateTime BeginTime { get; set; } |
|||
|
|||
public DateTime EndTime { get; set; } |
|||
|
|||
public Guid ProductId { get; set; } |
|||
|
|||
public Guid ProductSkuId { get; set; } |
|||
|
|||
public bool IsPublished { get; set; } |
|||
|
|||
public string ConcurrencyStamp { get; set; } |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos; |
|||
|
|||
[Serializable] |
|||
public class FlashSalesPlanGetListInput : ExtensiblePagedAndSortedResultRequestDto |
|||
{ |
|||
public Guid? StoreId { get; set; } |
|||
|
|||
public Guid? ProductId { get; set; } |
|||
|
|||
public Guid? ProductSkuId { get; set; } |
|||
|
|||
public bool OnlyShowPublished { get; set; } |
|||
|
|||
public DateTime? Start { get; set; } |
|||
|
|||
public DateTime? End { get; set; } |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Domain.Entities; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos; |
|||
|
|||
[Serializable] |
|||
public class FlashSalesPlanUpdateDto : ExtensibleEntityDto, IHasConcurrencyStamp |
|||
{ |
|||
public DateTime BeginTime { get; set; } |
|||
|
|||
public DateTime EndTime { get; set; } |
|||
|
|||
public Guid ProductId { get; set; } |
|||
|
|||
public Guid ProductSkuId { get; set; } |
|||
|
|||
public bool IsPublished { get; set; } |
|||
|
|||
public string ConcurrencyStamp { get; set; } |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
|
|||
public interface IFlashSalesPlanAppService : |
|||
ICrudAppService< |
|||
FlashSalesPlanDto, |
|||
Guid, |
|||
FlashSalesPlanGetListInput, |
|||
FlashSalesPlanCreateDto, |
|||
FlashSalesPlanUpdateDto> |
|||
{ |
|||
Task PreOrderAsync(Guid id); |
|||
|
|||
Task CheckPreOrderAsync(Guid id); |
|||
|
|||
Task CreateOrderAsync(Guid id, CreateOrderInput input); |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults.Dtos; |
|||
|
|||
public class FlashSalesResultDto : ExtensibleFullAuditedEntityDto<Guid> |
|||
{ |
|||
public virtual Guid StoreId { get; set; } |
|||
|
|||
public virtual Guid PlanId { get; set; } |
|||
|
|||
public virtual FlashSalesResultStatus Status { get; set; } |
|||
|
|||
public virtual string Reason { get; set; } |
|||
|
|||
public virtual Guid UserId { get; set; } |
|||
|
|||
public virtual Guid? OrderId { get; set; } |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults.Dtos; |
|||
|
|||
public class FlashSalesResultGetListInput : ExtensiblePagedAndSortedResultRequestDto |
|||
{ |
|||
public virtual Guid? StoreId { get; set; } |
|||
|
|||
public virtual Guid? PlanId { get; set; } |
|||
|
|||
public virtual FlashSalesResultStatus? Status { get; set; } |
|||
|
|||
public virtual Guid? UserId { get; set; } |
|||
|
|||
public virtual Guid? OrderId { get; set; } |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using System; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults.Dtos; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults; |
|||
|
|||
public interface IFlashSalesResultAppService : |
|||
IReadOnlyAppService< |
|||
FlashSalesResultDto, |
|||
Guid, |
|||
FlashSalesResultGetListInput> |
|||
{ |
|||
|
|||
} |
|||
@ -1,13 +1,28 @@ |
|||
using Volo.Abp.Reflection; |
|||
using Volo.Abp.Reflection; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.Permissions; |
|||
|
|||
public class FlashSalesPermissions |
|||
{ |
|||
public const string GroupName = "FlashSales"; |
|||
public const string GroupName = "EasyAbp.EShop.Plugins.FlashSales"; |
|||
|
|||
public static string[] GetAll() |
|||
{ |
|||
return ReflectionHelper.GetPublicConstantsRecursively(typeof(FlashSalesPermissions)); |
|||
} |
|||
|
|||
public class FlashSalesPlan |
|||
{ |
|||
public const string Default = GroupName + ".FlashSalesPlan"; |
|||
public const string Manage = Default + ".Manage"; |
|||
public const string Update = Default + ".Update"; |
|||
public const string Create = Default + ".Create"; |
|||
public const string Delete = Default + ".Delete"; |
|||
} |
|||
|
|||
public class FlashSalesResult |
|||
{ |
|||
public const string Default = GroupName + ".FlashSalesResult"; |
|||
public const string Manage = Default + ".Manage"; |
|||
} |
|||
} |
|||
|
|||
@ -1,13 +1,48 @@ |
|||
using AutoMapper; |
|||
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 |
|||
public class FlashSalesApplicationAutoMapperProfile : Profile, ISingletonDependency |
|||
{ |
|||
public FlashSalesApplicationAutoMapperProfile() |
|||
public FlashSalesApplicationAutoMapperProfile(IJsonSerializer jsonSerializer) |
|||
{ |
|||
/* 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>() |
|||
.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>() |
|||
.MapExtraProperties(); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,362 @@ |
|||
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 Microsoft.AspNetCore.Authorization; |
|||
using Microsoft.Extensions.Caching.Distributed; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Application.Services; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.DistributedLocking; |
|||
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; |
|||
|
|||
[Authorize] |
|||
public class FlashSalesPlanAppService : |
|||
CrudAppService<FlashSalesPlan, FlashSalesPlanDto, Guid, FlashSalesPlanGetListInput, FlashSalesPlanCreateDto, FlashSalesPlanUpdateDto>, |
|||
IFlashSalesPlanAppService |
|||
{ |
|||
protected override string GetPolicyName { get; set; } |
|||
protected override string GetListPolicyName { get; set; } |
|||
protected override string CreatePolicyName { get; set; } = FlashSalesPermissions.FlashSalesPlan.Create; |
|||
protected override string UpdatePolicyName { get; set; } = FlashSalesPermissions.FlashSalesPlan.Update; |
|||
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; } |
|||
|
|||
public FlashSalesPlanAppService( |
|||
IFlashSalesPlanRepository flashSalesPlanRepository, |
|||
IProductAppService productAppService, |
|||
IProductDetailAppService productDetailAppService, |
|||
IDistributedCache tokenDistributedCache, |
|||
IDistributedCache<FlashSalesPlanCacheItem, Guid> distributedCache, |
|||
IDistributedEventBus distributedEventBus, |
|||
FlashSalesPlanManager flashSalesPlanManager, |
|||
IFlashSalesResultRepository flashSalesResultRepository, |
|||
IAbpDistributedLock distributedLock) |
|||
: base(flashSalesPlanRepository) |
|||
{ |
|||
FlashSalesPlanRepository = flashSalesPlanRepository; |
|||
ProductAppService = productAppService; |
|||
ProductDetailAppService = productDetailAppService; |
|||
TokenDistributedCache = tokenDistributedCache; |
|||
DistributedCache = distributedCache; |
|||
DistributedEventBus = distributedEventBus; |
|||
FlashSalesPlanManager = flashSalesPlanManager; |
|||
FlashSalesResultRepository = flashSalesResultRepository; |
|||
DistributedLock = distributedLock; |
|||
} |
|||
|
|||
public override async Task<FlashSalesPlanDto> GetAsync(Guid id) |
|||
{ |
|||
await CheckGetPolicyAsync(); |
|||
|
|||
var flashSalesPlan = await GetEntityByIdAsync(id); |
|||
|
|||
if (!flashSalesPlan.IsPublished) |
|||
{ |
|||
await CheckPolicyAsync(FlashSalesPermissions.FlashSalesPlan.Manage); |
|||
} |
|||
|
|||
return await MapToGetOutputDtoAsync(flashSalesPlan); |
|||
} |
|||
|
|||
public override async Task<FlashSalesPlanDto> CreateAsync(FlashSalesPlanCreateDto input) |
|||
{ |
|||
await CheckCreatePolicyAsync(); |
|||
|
|||
var flashSalesPlan = await FlashSalesPlanManager.CreateAsync( |
|||
input.StoreId, |
|||
input.BeginTime, |
|||
input.EndTime, |
|||
input.ProductId, |
|||
input.ProductSkuId, |
|||
input.IsPublished |
|||
); |
|||
|
|||
await FlashSalesPlanRepository.InsertAsync(flashSalesPlan, autoSave: true); |
|||
|
|||
return await MapToGetOutputDtoAsync(flashSalesPlan); |
|||
} |
|||
|
|||
public override async Task<FlashSalesPlanDto> UpdateAsync(Guid id, FlashSalesPlanUpdateDto input) |
|||
{ |
|||
await CheckUpdatePolicyAsync(); |
|||
|
|||
var flashSalesPlan = await GetEntityByIdAsync(id); |
|||
|
|||
await FlashSalesPlanManager.UpdateAsync( |
|||
flashSalesPlan, |
|||
input.BeginTime, |
|||
input.EndTime, |
|||
input.ProductId, |
|||
input.ProductSkuId, |
|||
input.IsPublished |
|||
); |
|||
|
|||
flashSalesPlan.SetConcurrencyStampIfNotNull(input.ConcurrencyStamp); |
|||
|
|||
await FlashSalesPlanRepository.UpdateAsync(flashSalesPlan, autoSave: true); |
|||
|
|||
return await MapToGetOutputDtoAsync(flashSalesPlan); |
|||
} |
|||
|
|||
protected override async Task<IQueryable<FlashSalesPlan>> CreateFilteredQueryAsync(FlashSalesPlanGetListInput input) |
|||
{ |
|||
if (!input.OnlyShowPublished) |
|||
{ |
|||
await CheckPolicyAsync(FlashSalesPermissions.FlashSalesPlan.Manage); |
|||
} |
|||
|
|||
return (await base.CreateFilteredQueryAsync(input)) |
|||
.WhereIf(input.StoreId.HasValue, x => x.StoreId == input.StoreId.Value) |
|||
.WhereIf(input.ProductId.HasValue, x => x.ProductId == input.ProductId.Value) |
|||
.WhereIf(input.ProductSkuId.HasValue, x => x.ProductSkuId == input.ProductSkuId.Value) |
|||
.WhereIf(input.OnlyShowPublished, x => x.IsPublished) |
|||
.WhereIf(input.Start.HasValue, x => x.BeginTime >= input.Start.Value) |
|||
.WhereIf(input.End.HasValue, x => x.BeginTime <= input.End.Value); |
|||
} |
|||
|
|||
public virtual async Task PreOrderAsync(Guid id) |
|||
{ |
|||
var plan = await GetFlashSalesPlanCacheAsync(id); |
|||
var product = await ProductAppService.GetAsync(plan.ProductId); |
|||
var productSku = product.GetSkuById(plan.ProductSkuId); |
|||
|
|||
if (!product.IsPublished) |
|||
{ |
|||
throw new BusinessException(FlashSalesErrorCodes.ProductIsNotPublished); |
|||
} |
|||
|
|||
if (product.InventoryStrategy != InventoryStrategy.FlashSales) |
|||
{ |
|||
throw new BusinessException(FlashSalesErrorCodes.IsNotFlashSalesProduct); |
|||
} |
|||
|
|||
if (!plan.IsPublished) |
|||
{ |
|||
throw new EntityNotFoundException(typeof(FlashSalesPlan), id); |
|||
} |
|||
|
|||
if (Clock.Now > plan.EndTime) |
|||
{ |
|||
throw new BusinessException(FlashSalesErrorCodes.FlashSalesPlanIsExpired); |
|||
} |
|||
|
|||
await SetCacheHashTokenAsync(plan, product, productSku); |
|||
} |
|||
|
|||
public virtual async Task CheckPreOrderAsync(Guid id) |
|||
{ |
|||
var plan = await GetFlashSalesPlanCacheAsync(id); |
|||
if (Clock.Now > plan.EndTime) |
|||
{ |
|||
throw new BusinessException(FlashSalesErrorCodes.FlashSalesPlanIsExpired); |
|||
} |
|||
|
|||
var cacheHashToken = await GetCacheHashTokenAsync(plan); |
|||
if (cacheHashToken.IsNullOrWhiteSpace()) |
|||
{ |
|||
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); |
|||
} |
|||
} |
|||
|
|||
public virtual async Task CreateOrderAsync(Guid id, CreateOrderInput input) |
|||
{ |
|||
var plan = await GetFlashSalesPlanCacheAsync(id); |
|||
var now = Clock.Now; |
|||
if (plan.BeginTime > now) |
|||
{ |
|||
throw new BusinessException(FlashSalesErrorCodes.ProductIsNotPublished); |
|||
} |
|||
|
|||
if (now > plan.EndTime) |
|||
{ |
|||
throw new BusinessException(FlashSalesErrorCodes.FlashSalesPlanIsExpired); |
|||
} |
|||
|
|||
var cacheHashToken = await GetCacheHashTokenAsync(plan); |
|||
if (cacheHashToken.IsNullOrWhiteSpace()) |
|||
{ |
|||
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); |
|||
|
|||
await DistributedEventBus.PublishAsync(CreateFlashSalesOrderEto); |
|||
} |
|||
|
|||
protected virtual async Task<CreateFlashSalesOrderEto> PrepareCreateFlashSalesOrderEtoAsync( |
|||
FlashSalesPlanDto plan, |
|||
ProductDto product, |
|||
ProductSkuDto productSku, |
|||
FlashSalesResult result, |
|||
CreateOrderInput input, |
|||
Guid userId, |
|||
DateTime now) |
|||
{ |
|||
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); |
|||
planEto.TenantId = CurrentTenant.Id; |
|||
|
|||
var eto = new CreateFlashSalesOrderEto() |
|||
{ |
|||
TenantId = CurrentTenant.Id, |
|||
PlanId = plan.Id, |
|||
UserId = userId, |
|||
PendingResultId = result.Id, |
|||
StoreId = plan.StoreId, |
|||
CreateTime = now, |
|||
CustomerRemark = input.CustomerRemark, |
|||
Quantity = 1,//should configure
|
|||
Product = productEto, |
|||
ProductDetail = productDetail, |
|||
Plan = planEto |
|||
}; |
|||
|
|||
foreach (var item in input.ExtraProperties) |
|||
{ |
|||
eto.ExtraProperties.Add(item.Key, item.Value); |
|||
} |
|||
|
|||
return eto; |
|||
} |
|||
|
|||
protected virtual async Task<FlashSalesPlanCacheItem> GetFlashSalesPlanCacheAsync(Guid id) |
|||
{ |
|||
return await DistributedCache.GetOrAddAsync(id, async () => |
|||
{ |
|||
var flashSalesPlan = await FlashSalesPlanRepository.GetAsync(id); |
|||
return ObjectMapper.Map<FlashSalesPlan, FlashSalesPlanCacheItem>(flashSalesPlan); |
|||
}); |
|||
} |
|||
|
|||
protected virtual Task<string> GetCacheKeyAsync(FlashSalesPlanDto plan) |
|||
{ |
|||
return Task.FromResult($"eshopflashsales_{CurrentUser.Id}_{plan.ProductSkuId}"); |
|||
} |
|||
|
|||
protected virtual async Task<string> GetCacheHashTokenAsync(FlashSalesPlanDto plan) |
|||
{ |
|||
return await TokenDistributedCache.GetStringAsync(await GetCacheKeyAsync(plan)); |
|||
} |
|||
|
|||
protected virtual async Task RemoveCacheHashTokenAsync(FlashSalesPlanDto plan) |
|||
{ |
|||
await TokenDistributedCache.RemoveAsync(await GetCacheKeyAsync(plan)); |
|||
} |
|||
|
|||
protected virtual async Task SetCacheHashTokenAsync(FlashSalesPlanDto plan, ProductDto product, ProductSkuDto productSku) |
|||
{ |
|||
await TokenDistributedCache.SetStringAsync(await GetCacheKeyAsync(plan), await GetHashTokenAsync(plan, product, productSku), 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) |
|||
{ |
|||
if (cacheHashToken.IsNullOrWhiteSpace()) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
var hashToken = await GetHashTokenAsync(plan, product, productSku); |
|||
|
|||
return cacheHashToken == hashToken; |
|||
} |
|||
|
|||
protected virtual async Task<FlashSalesResult> CreatePendingFlashSalesResultAsync(FlashSalesPlanDto plan, Guid userId) |
|||
{ |
|||
var lockKey = $"create-flash-sales-order-{plan.Id}-{userId}"; |
|||
|
|||
await using var handle = await DistributedLock.TryAcquireAsync(lockKey); |
|||
|
|||
if (handle == null) |
|||
{ |
|||
throw new BusinessException(FlashSalesErrorCodes.CreateFlashSalesOrderBusy); |
|||
} |
|||
|
|||
// Prevent repeat submit
|
|||
if (await FlashSalesResultRepository.AnyAsync(x => x.PlanId == plan.Id && x.UserId == userId)) |
|||
{ |
|||
throw new BusinessException(FlashSalesErrorCodes.AlreadySubmitCreateFlashSalesOrder); |
|||
} |
|||
|
|||
var result = new FlashSalesResult( |
|||
id: GuidGenerator.Create(), |
|||
tenantId: CurrentTenant.Id, |
|||
storeId: plan.StoreId, |
|||
planId: plan.Id, |
|||
status: FlashSalesResultStatus.Pending, |
|||
reason: null, |
|||
userId: userId, |
|||
orderId: null |
|||
); |
|||
return await FlashSalesResultRepository.InsertAsync(result, autoSave: true); |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.Domain.Entities.Events; |
|||
using Volo.Abp.EventBus; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
|
|||
public class FlashSalesPlanCacheInvalidator : ILocalEventHandler<EntityChangedEventData<FlashSalesPlan>> |
|||
{ |
|||
protected IDistributedCache<FlashSalesPlanCacheItem, Guid> DistributedCache { get; } |
|||
|
|||
public virtual async Task HandleEventAsync(EntityChangedEventData<FlashSalesPlan> eventData) |
|||
{ |
|||
await DistributedCache.RemoveAsync(eventData.Entity.Id); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
|
|||
[Serializable] |
|||
public class FlashSalesPlanCacheItem : FlashSalesPlanDto |
|||
{ |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults.Dtos; |
|||
using EasyAbp.EShop.Plugins.FlashSales.Permissions; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults; |
|||
|
|||
public class FlashSalesResultAppService : |
|||
ReadOnlyAppService<FlashSalesResult, FlashSalesResultDto, Guid, FlashSalesResultGetListInput>, |
|||
IFlashSalesResultAppService |
|||
{ |
|||
protected override string GetPolicyName { get; set; } |
|||
protected override string GetListPolicyName { get; set; } |
|||
|
|||
protected IFlashSalesResultRepository FlashSalesResultRepository { get; } |
|||
|
|||
public FlashSalesResultAppService(IFlashSalesResultRepository flashSalesResultRepository) : base(flashSalesResultRepository) |
|||
{ |
|||
} |
|||
|
|||
public override async Task<FlashSalesResultDto> GetAsync(Guid id) |
|||
{ |
|||
await CheckGetPolicyAsync(); |
|||
|
|||
var flashSalesPlan = await GetEntityByIdAsync(id); |
|||
|
|||
if (flashSalesPlan.UserId != CurrentUser.Id) |
|||
{ |
|||
await CheckPolicyAsync(FlashSalesPermissions.FlashSalesResult.Manage); |
|||
} |
|||
|
|||
return await MapToGetOutputDtoAsync(flashSalesPlan); |
|||
} |
|||
|
|||
protected override async Task<IQueryable<FlashSalesResult>> CreateFilteredQueryAsync(FlashSalesResultGetListInput input) |
|||
{ |
|||
if (input.UserId != CurrentUser.Id) |
|||
{ |
|||
await CheckPolicyAsync(FlashSalesPermissions.FlashSalesResult.Manage); |
|||
} |
|||
|
|||
return (await base.CreateFilteredQueryAsync(input)) |
|||
.WhereIf(input.StoreId.HasValue, x => x.StoreId == input.StoreId.Value) |
|||
.WhereIf(input.PlanId.HasValue, x => x.PlanId == input.PlanId.Value) |
|||
.WhereIf(input.Status.HasValue, x => x.Status == input.Status.Value) |
|||
.WhereIf(input.UserId.HasValue, x => x.UserId == input.UserId.Value) |
|||
.WhereIf(input.OrderId.HasValue, x => x.OrderId == input.OrderId.Value); |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using System; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.ObjectExtending; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
|
|||
public class CreateFlashSalesOrderCompleteEto : ExtensibleObject, IMultiTenant |
|||
{ |
|||
public Guid? TenantId { get; set; } |
|||
|
|||
public Guid PendingResultId { get; set; } |
|||
|
|||
public bool Success { get; set; } |
|||
|
|||
public Guid StoreId { get; set; } |
|||
|
|||
public Guid PlanId { get; set; } |
|||
|
|||
public string Reason { get; set; } |
|||
|
|||
public Guid UserId { get; set; } |
|||
|
|||
public Guid? OrderId { get; set; } |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using System; |
|||
using EasyAbp.EShop.Products.Products; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.ObjectExtending; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
|
|||
[Serializable] |
|||
public class CreateFlashSalesOrderEto : 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 FlashSalesProductEto Product { get; set; } |
|||
|
|||
public FlashSalesProductDetailEto ProductDetail { get; set; } |
|||
|
|||
public FlashSalesPlanEto Plan { get; set; } |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
using System; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.ObjectExtending; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
|
|||
public class FlashSalesPlanEto : FullAuditedEntityEto<Guid>, IMultiTenant |
|||
{ |
|||
public Guid? TenantId { get; set; } |
|||
|
|||
public Guid StoreId { get; set; } |
|||
|
|||
public DateTime BeginTime { get; set; } |
|||
|
|||
public DateTime EndTime { get; set; } |
|||
|
|||
public Guid ProductId { get; set; } |
|||
|
|||
public Guid ProductSkuId { get; set; } |
|||
|
|||
public bool IsPublished { get; set; } |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using EasyAbp.EShop.Products.Products; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
|
|||
public class FlashSalesProductAttributeEto : FullAuditedEntityEto<Guid>, IProductAttribute |
|||
{ |
|||
public string DisplayName { get; set; } |
|||
|
|||
public string Description { get; set; } |
|||
|
|||
public int DisplayOrder { get; set; } |
|||
|
|||
public List<FlashSalesProductAttributeOptionEto> ProductAttributeOptions { get; set; } |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System; |
|||
using EasyAbp.EShop.Products.Products; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
|
|||
public class FlashSalesProductAttributeOptionEto : FullAuditedEntityEto<Guid>, IProductAttributeOption |
|||
{ |
|||
public string DisplayName { get; set; } |
|||
|
|||
public string Description { get; set; } |
|||
|
|||
public int DisplayOrder { get; set; } |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using EasyAbp.EShop.Products.Products; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
|
|||
public class FlashSalesProductEto : FullAuditedEntityEto<Guid>, IProduct, IMultiTenant |
|||
{ |
|||
public Guid? TenantId { get; set; } |
|||
|
|||
public Guid StoreId { get; set; } |
|||
|
|||
public string ProductGroupName { get; set; } |
|||
|
|||
public Guid? ProductDetailId { get; set; } |
|||
|
|||
public string UniqueName { get; set; } |
|||
|
|||
public string DisplayName { get; set; } |
|||
|
|||
public InventoryStrategy InventoryStrategy { get; set; } |
|||
|
|||
public string InventoryProviderName { get; set; } |
|||
|
|||
public string MediaResources { get; set; } |
|||
|
|||
public int DisplayOrder { get; set; } |
|||
|
|||
public bool IsPublished { get; set; } |
|||
|
|||
public bool IsStatic { get; set; } |
|||
|
|||
public bool IsHidden { get; set; } |
|||
|
|||
public List<FlashSalesProductAttributeEto> ProductAttributes { get; set; } |
|||
|
|||
public List<FlashSalesProductSkuEto> ProductSkus { get; set; } |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using System; |
|||
using EasyAbp.EShop.Products.Products; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
|
|||
public class FlashSalesProductSkuEto : FullAuditedEntityEto<Guid>, IProductSku |
|||
{ |
|||
public string SerializedAttributeOptionIds { get; set; } |
|||
|
|||
public string Name { get; set; } |
|||
|
|||
public string Currency { get; set; } |
|||
|
|||
public decimal? OriginalPrice { get; set; } |
|||
|
|||
public decimal Price { get; set; } |
|||
|
|||
public int OrderMinQuantity { get; set; } |
|||
|
|||
public int OrderMaxQuantity { get; set; } |
|||
|
|||
public string MediaResources { get; set; } |
|||
|
|||
public Guid? ProductDetailId { get; set; } |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using System; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
|
|||
public class FlashSalesProductDetailEto : FullAuditedEntityEto<Guid> |
|||
{ |
|||
public Guid? StoreId { get; set; } |
|||
|
|||
public string Description { get; set; } |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults; |
|||
|
|||
public enum FlashSalesResultStatus |
|||
{ |
|||
Pending, |
|||
Successful, |
|||
Failed |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
using System; |
|||
using Volo.Abp.ObjectExtending; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales; |
|||
|
|||
public class FullAuditedEntityEto<TPrimaryKey> : ExtensibleObject |
|||
{ |
|||
public TPrimaryKey Id { get; set; } |
|||
|
|||
public DateTime CreationTime { get; set; } |
|||
|
|||
public Guid? CreatorId { get; set; } |
|||
|
|||
public DateTime? LastModificationTime { get; set; } |
|||
|
|||
public Guid? LastModifierId { get; set; } |
|||
|
|||
public bool IsDeleted { get; set; } |
|||
|
|||
public Guid? DeleterId { get; set; } |
|||
|
|||
public DateTime? DeletionTime { get; set; } |
|||
} |
|||
@ -1,5 +1,34 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"EasyAbp.EShop.Plugins.FlashSales:EndTimeMustBeLaterThanBeginTime": "End time must be later than start time", |
|||
"Menu:FlashSalesManagement": "FlashSalesManagement", |
|||
"Permission:FlashSalesPlan": "FlashSalesPlan", |
|||
"Permission:Manage": "Manage", |
|||
"Permission:Create": "Create", |
|||
"Permission:Update": "Update", |
|||
"Permission:Delete": "Delete", |
|||
"Menu:FlashSalesPlan": "FlashSalesPlan", |
|||
"FlashSalesPlan": "FlashSalesPlan", |
|||
"FlashSalesPlanStoreId": "Store ID", |
|||
"FlashSalesPlanBeginTime": "Begin Time", |
|||
"FlashSalesPlanEndTime": "End Time", |
|||
"FlashSalesPlanProductId": "Product ID", |
|||
"FlashSalesPlanProductSkuId": "Product SKU ID", |
|||
"FlashSalesPlanIsPublished": "Is Published", |
|||
"CreateFlashSalesPlan": "Create", |
|||
"EditFlashSalesPlan": "Edit", |
|||
"FlashSalesPlanDeletionConfirmationMessage": "Are you sure to delete the flashSalesPlan {0}?", |
|||
"SuccessfullyDeleted": "Successfully deleted", |
|||
"Permission:FlashSalesResult": "FlashSalesResult", |
|||
"Menu:FlashSalesResult": "FlashSalesResult", |
|||
"FlashSalesResult": "FlashSalesResult", |
|||
"FlashSalesResultStoreId": "Store ID", |
|||
"FlashSalesResultPlanId": "Plan ID", |
|||
"FlashSalesResultStatus": "Status", |
|||
"FlashSalesResultUserId": "User ID", |
|||
"FlashSalesResultOrderId": "Order ID", |
|||
"FlashSalesResultCreationTime": "Creation Time", |
|||
"ViewFlashSalesPlan": "View" |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -1,5 +1,34 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"EasyAbp.EShop.Plugins.FlashSales:EndTimeMustBeLaterThanBeginTime": "结束时间必须晚于开始时间", |
|||
"Menu:FlashSalesManagement": "闪购", |
|||
"Permission:FlashSalesPlan": "闪购计划", |
|||
"Permission:Manage": "管理", |
|||
"Permission:Create": "新建", |
|||
"Permission:Update": "更新", |
|||
"Permission:Delete": "删除", |
|||
"Menu:FlashSalesPlan": "闪购计划", |
|||
"FlashSalesPlan": "闪购计划", |
|||
"FlashSalesPlanStoreId": "店铺 ID", |
|||
"FlashSalesPlanBeginTime": "开始时间", |
|||
"FlashSalesPlanEndTime": "结束时间", |
|||
"FlashSalesPlanProductId": "产品 ID", |
|||
"FlashSalesPlanProductSkuId": "产品SKU ID", |
|||
"FlashSalesPlanIsPublished": "发布", |
|||
"CreateFlashSalesPlan": "新建", |
|||
"EditFlashSalesPlan": "编辑", |
|||
"FlashSalesPlanDeletionConfirmationMessage": "确认删除闪购计划 {0}?", |
|||
"SuccessfullyDeleted": "删除成功", |
|||
"Permission:FlashSalesResult": "闪购结果", |
|||
"Menu:FlashSalesResult": "闪购结果", |
|||
"FlashSalesResult": "闪购结果", |
|||
"FlashSalesResultStoreId": "店铺 ID", |
|||
"FlashSalesResultPlanId": "计划 ID", |
|||
"FlashSalesResultStatus": "状态", |
|||
"FlashSalesResultUserId": "用户 ID", |
|||
"FlashSalesResultOrderId": "订单 ID", |
|||
"FlashSalesResultCreationTime": "创建时间", |
|||
"ViewFlashSalesPlan": "查看" |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -1,5 +1,34 @@ |
|||
{ |
|||
"culture": "zh-Hant", |
|||
"texts": { |
|||
"EasyAbp.EShop.Plugins.FlashSales:EndTimeMustBeLaterThanBeginTime": "結束時間必須晚於開始時間", |
|||
"Menu:FlashSalesManagement": "FlashSalesManagement", |
|||
"Permission:FlashSalesPlan": "FlashSalesPlan", |
|||
"Permission:Manage": "Manage", |
|||
"Permission:Create": "Create", |
|||
"Permission:Update": "Update", |
|||
"Permission:Delete": "Delete", |
|||
"Menu:FlashSalesPlan": "FlashSalesPlan", |
|||
"FlashSalesPlan": "FlashSalesPlan", |
|||
"FlashSalesPlanStoreId": "FlashSalesPlanStoreId", |
|||
"FlashSalesPlanBeginTime": "FlashSalesPlanBeginTime", |
|||
"FlashSalesPlanEndTime": "FlashSalesPlanEndTime", |
|||
"FlashSalesPlanProductId": "FlashSalesPlanProductId", |
|||
"FlashSalesPlanProductSkuId": "FlashSalesPlanProductSkuId", |
|||
"FlashSalesPlanIsPublished": "FlashSalesPlanIsActive", |
|||
"CreateFlashSalesPlan": "CreateFlashSalesPlan", |
|||
"EditFlashSalesPlan": "EditFlashSalesPlan", |
|||
"FlashSalesPlanDeletionConfirmationMessage": "Are you sure to delete the flashSalesPlan {0}?", |
|||
"SuccessfullyDeleted": "Successfully deleted", |
|||
"Permission:FlashSalesResult": "闪购结果", |
|||
"Menu:FlashSalesResult": "闪购计划", |
|||
"FlashSalesResult": "闪购结果", |
|||
"FlashSalesResultStoreId": "店铺 ID", |
|||
"FlashSalesResultPlanId": "计划 ID", |
|||
"FlashSalesResultStatus": "状态", |
|||
"FlashSalesResultUserId": "用户 ID", |
|||
"FlashSalesResultOrderId": "订单 ID", |
|||
"FlashSalesResultCreationTime": "创建时间", |
|||
"ViewFlashSalesPlan": "查看" |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,16 @@ |
|||
using AutoMapper; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales; |
|||
|
|||
public class EShopPluginsFlashSalesDomainAutoMapperProfile : Profile |
|||
{ |
|||
public EShopPluginsFlashSalesDomainAutoMapperProfile() |
|||
{ |
|||
/* 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, FlashSalesPlanEto>() |
|||
.MapExtraProperties(); |
|||
} |
|||
} |
|||
@ -1,13 +1,33 @@ |
|||
using Volo.Abp.Domain; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.AutoMapper; |
|||
using Volo.Abp.Domain; |
|||
using Volo.Abp.Domain.Entities.Events.Distributed; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpDddDomainModule), |
|||
typeof(AbpAutoMapperModule), |
|||
typeof(EShopPluginsFlashSalesDomainSharedModule) |
|||
)] |
|||
public class EShopPluginsFlashSalesDomainModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddAutoMapperObjectMapper<EShopPluginsFlashSalesDomainModule>(); |
|||
|
|||
Configure<AbpAutoMapperOptions>(options => |
|||
{ |
|||
options.AddProfile<EShopPluginsFlashSalesDomainAutoMapperProfile>(validate: true); |
|||
}); |
|||
|
|||
Configure<AbpDistributedEntityEventOptions>(options => |
|||
{ |
|||
options.EtoMappings.Add<FlashSalesPlan, FlashSalesPlanEto>(typeof(EShopPluginsFlashSalesDomainModule)); |
|||
|
|||
options.AutoEventSelectors.Add<FlashSalesPlan>(); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,12 @@ |
|||
using Volo.Abp; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
|
|||
internal class EndTimeMustBeLaterThanBeginTimeException : BusinessException |
|||
{ |
|||
public EndTimeMustBeLaterThanBeginTimeException() |
|||
: base(FlashSalesErrorCodes.EndTimeMustBeLaterThanBeginTime) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
using System; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
|
|||
public class FlashSalesPlan : FullAuditedAggregateRoot<Guid>, IMultiTenant |
|||
{ |
|||
public virtual Guid? TenantId { get; protected set; } |
|||
|
|||
public virtual Guid StoreId { get; protected set; } |
|||
|
|||
public virtual DateTime BeginTime { get; set; } |
|||
|
|||
public virtual DateTime EndTime { get; set; } |
|||
|
|||
public virtual Guid ProductId { get; protected set; } |
|||
|
|||
public virtual Guid ProductSkuId { get; protected set; } |
|||
|
|||
public virtual bool IsPublished { get; protected set; } |
|||
|
|||
protected FlashSalesPlan() |
|||
{ |
|||
} |
|||
|
|||
public FlashSalesPlan(Guid id, Guid? tenantId, Guid storeId, DateTime beginTime, DateTime endTime, Guid productId, Guid productSkuId, bool isPublished) |
|||
: base(id) |
|||
{ |
|||
TenantId = tenantId; |
|||
StoreId = storeId; |
|||
SetTimeRange(beginTime, endTime); |
|||
SetProduct(productId, productSkuId); |
|||
SetPublished(isPublished); |
|||
} |
|||
|
|||
public void SetTimeRange(DateTime beginTime, DateTime endTime) |
|||
{ |
|||
if (beginTime > endTime) |
|||
{ |
|||
throw new EndTimeMustBeLaterThanBeginTimeException(); |
|||
} |
|||
|
|||
BeginTime = beginTime; |
|||
EndTime = endTime; |
|||
} |
|||
|
|||
public void SetProduct(Guid productId, Guid productSkuId) |
|||
{ |
|||
ProductId = productId; |
|||
ProductSkuId = productSkuId; |
|||
} |
|||
|
|||
public void SetPublished(bool isPublished) |
|||
{ |
|||
IsPublished = isPublished; |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Services; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
|
|||
public class FlashSalesPlanManager : DomainService |
|||
{ |
|||
public virtual Task<FlashSalesPlan> CreateAsync( |
|||
Guid storeId, |
|||
DateTime beginTime, |
|||
DateTime endTime, |
|||
Guid productId, |
|||
Guid productSkuId, |
|||
bool isActive) |
|||
{ |
|||
var flashSalesPlan = new FlashSalesPlan( |
|||
GuidGenerator.Create(), |
|||
CurrentTenant.Id, |
|||
storeId, |
|||
beginTime, |
|||
endTime, |
|||
productId, |
|||
productSkuId, |
|||
isActive |
|||
); |
|||
return Task.FromResult(flashSalesPlan); |
|||
} |
|||
|
|||
public virtual Task<FlashSalesPlan> UpdateAsync( |
|||
FlashSalesPlan flashSalesPlan, |
|||
DateTime beginTime, |
|||
DateTime endTime, |
|||
Guid productId, |
|||
Guid productSkuId, |
|||
bool isPublished) |
|||
{ |
|||
flashSalesPlan.SetTimeRange(beginTime, endTime); |
|||
flashSalesPlan.SetProduct(productId, productSkuId); |
|||
flashSalesPlan.SetPublished(isPublished); |
|||
|
|||
return Task.FromResult(flashSalesPlan); |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
|
|||
public interface IFlashSalesPlanRepository : IRepository<FlashSalesPlan, Guid> |
|||
{ |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
using Volo.Abp.Guids; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults; |
|||
|
|||
public class CreateFlashSalesOrderCompleteEventHandler : IDistributedEventHandler<CreateFlashSalesOrderCompleteEto>, ITransientDependency |
|||
{ |
|||
protected IFlashSalesResultRepository FlashSalesResultRepository { get; } |
|||
protected IGuidGenerator GuidGenerator { get; } |
|||
|
|||
public CreateFlashSalesOrderCompleteEventHandler(IFlashSalesResultRepository flashSalesResultRepository, IGuidGenerator guidGenerator) |
|||
{ |
|||
FlashSalesResultRepository = flashSalesResultRepository; |
|||
GuidGenerator = guidGenerator; |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public virtual async Task HandleEventAsync(CreateFlashSalesOrderCompleteEto eventData) |
|||
{ |
|||
var flashSalesResult = await FlashSalesResultRepository.GetAsync(eventData.PendingResultId); |
|||
|
|||
if (eventData.Success) |
|||
{ |
|||
flashSalesResult.MarkAsSuccessful(eventData.OrderId.Value); |
|||
} |
|||
else |
|||
{ |
|||
flashSalesResult.MarkAsFailed(eventData.Reason); |
|||
} |
|||
|
|||
await FlashSalesResultRepository.UpdateAsync(flashSalesResult, autoSave: true); |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
using System; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults; |
|||
|
|||
public class FlashSalesResult : FullAuditedAggregateRoot<Guid>, IMultiTenant |
|||
{ |
|||
public virtual Guid? TenantId { get; protected set; } |
|||
|
|||
public virtual Guid StoreId { get; protected set; } |
|||
|
|||
public virtual Guid PlanId { get; protected set; } |
|||
|
|||
public virtual FlashSalesResultStatus Status { get; protected set; } |
|||
|
|||
public virtual string Reason { get; protected set; } |
|||
|
|||
public virtual Guid UserId { get; protected set; } |
|||
|
|||
public virtual Guid? OrderId { get; protected set; } |
|||
|
|||
protected FlashSalesResult() { } |
|||
|
|||
public FlashSalesResult(Guid id, Guid? tenantId, Guid storeId, Guid planId, FlashSalesResultStatus status, string reason, Guid userId, Guid? orderId) |
|||
: base(id) |
|||
{ |
|||
TenantId = tenantId; |
|||
StoreId = storeId; |
|||
PlanId = planId; |
|||
Status = status; |
|||
Reason = reason; |
|||
UserId = userId; |
|||
OrderId = orderId; |
|||
} |
|||
|
|||
public void MarkAsSuccessful(Guid orderId) |
|||
{ |
|||
Status = FlashSalesResultStatus.Successful; |
|||
OrderId = orderId; |
|||
} |
|||
|
|||
public void MarkAsFailed(string reason) |
|||
{ |
|||
Status = FlashSalesResultStatus.Failed; |
|||
Reason = Check.NotNullOrEmpty(reason, nameof(reason)); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults; |
|||
|
|||
public interface IFlashSalesResultRepository : IRepository<FlashSalesResult, Guid> |
|||
{ |
|||
|
|||
} |
|||
@ -1,33 +1,32 @@ |
|||
using Microsoft.EntityFrameworkCore; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Volo.Abp; |
|||
using Volo.Abp.EntityFrameworkCore.Modeling; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore; |
|||
|
|||
public static class FlashSalesDbContextModelCreatingExtensions |
|||
{ |
|||
public static void ConfigureFlashSales( |
|||
public static void ConfigureEShopPluginsFlashSales( |
|||
this ModelBuilder builder) |
|||
{ |
|||
Check.NotNull(builder, nameof(builder)); |
|||
|
|||
/* Configure all entities here. Example: |
|||
|
|||
builder.Entity<Question>(b => |
|||
builder.Entity<FlashSalesPlan>(b => |
|||
{ |
|||
//Configure table & schema name
|
|||
b.ToTable(FlashSalesDbProperties.DbTablePrefix + "Questions", FlashSalesDbProperties.DbSchema); |
|||
|
|||
b.ToTable(FlashSalesDbProperties.DbTablePrefix + "Plans", FlashSalesDbProperties.DbSchema); |
|||
b.ConfigureByConvention(); |
|||
|
|||
//Properties
|
|||
b.Property(q => q.Title).IsRequired().HasMaxLength(QuestionConsts.MaxTitleLength); |
|||
/* Configure more properties here */ |
|||
}); |
|||
|
|||
//Relations
|
|||
b.HasMany(question => question.Tags).WithOne().HasForeignKey(qt => qt.QuestionId); |
|||
builder.Entity<FlashSalesResult>(b => |
|||
{ |
|||
b.ToTable(FlashSalesDbProperties.DbTablePrefix + "Results", FlashSalesDbProperties.DbSchema); |
|||
b.ConfigureByConvention(); |
|||
|
|||
//Indexes
|
|||
b.HasIndex(q => q.CreationTime); |
|||
/* Configure more properties here */ |
|||
}); |
|||
*/ |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,21 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore; |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans |
|||
{ |
|||
public class EfCoreFlashSalesPlanRepository : EfCoreRepository<IFlashSalesDbContext, FlashSalesPlan, Guid>, IFlashSalesPlanRepository |
|||
{ |
|||
public EfCoreFlashSalesPlanRepository(IDbContextProvider<IFlashSalesDbContext> dbContextProvider) : base(dbContextProvider) |
|||
{ |
|||
} |
|||
|
|||
public override async Task<IQueryable<FlashSalesPlan>> WithDetailsAsync() |
|||
{ |
|||
return (await GetQueryableAsync()).IncludeDetails(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System.Linq; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
|
|||
public static class FlashSalesPlanEfCoreQueryableExtensions |
|||
{ |
|||
public static IQueryable<FlashSalesPlan> IncludeDetails(this IQueryable<FlashSalesPlan> queryable, bool include = true) |
|||
{ |
|||
if (!include) |
|||
{ |
|||
return queryable; |
|||
} |
|||
|
|||
return queryable |
|||
// .Include(x => x.xxx) // TODO: AbpHelper generated
|
|||
; |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore; |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults; |
|||
|
|||
public class EfCoreFlashSalesResultRepository : EfCoreRepository<IFlashSalesDbContext, FlashSalesResult, Guid>, IFlashSalesResultRepository |
|||
{ |
|||
public EfCoreFlashSalesResultRepository(IDbContextProvider<IFlashSalesDbContext> dbContextProvider) : base(dbContextProvider) |
|||
{ |
|||
} |
|||
|
|||
public override async Task<IQueryable<FlashSalesResult>> WithDetailsAsync() |
|||
{ |
|||
return (await GetQueryableAsync()).IncludeDetails(); |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using System.Linq; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults; |
|||
|
|||
public static class FlashSalesResultEfCoreQuerableExtensions |
|||
{ |
|||
public static IQueryable<FlashSalesResult> IncludeDetails(this IQueryable<FlashSalesResult> queryable, bool include = true) |
|||
{ |
|||
if (!include) |
|||
{ |
|||
return queryable; |
|||
} |
|||
|
|||
return queryable; |
|||
} |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
|
|||
[Area(EShopPluginsFlashSalesRemoteServiceConsts.ModuleName)] |
|||
[RemoteService(Name = EShopPluginsFlashSalesRemoteServiceConsts.RemoteServiceName)] |
|||
[Route("/api/e-shop/plugins/flash-sales/flash-sales-plan")] |
|||
public class FlashSalesPlanController : |
|||
FlashSalesController, |
|||
IFlashSalesPlanAppService |
|||
{ |
|||
protected IFlashSalesPlanAppService Service { get; } |
|||
|
|||
public FlashSalesPlanController(IFlashSalesPlanAppService flashSalesPlanAppService) |
|||
{ |
|||
Service = flashSalesPlanAppService; |
|||
} |
|||
|
|||
[HttpGet("{id}")] |
|||
public virtual Task<FlashSalesPlanDto> GetAsync(Guid id) |
|||
{ |
|||
return Service.GetAsync(id); |
|||
} |
|||
|
|||
[HttpGet] |
|||
public virtual Task<PagedResultDto<FlashSalesPlanDto>> GetListAsync(FlashSalesPlanGetListInput input) |
|||
{ |
|||
return Service.GetListAsync(input); |
|||
} |
|||
|
|||
[HttpPost("{id}")] |
|||
public virtual Task<FlashSalesPlanDto> CreateAsync(FlashSalesPlanCreateDto input) |
|||
{ |
|||
return Service.CreateAsync(input); |
|||
} |
|||
|
|||
[HttpPut("{id}")] |
|||
public virtual Task<FlashSalesPlanDto> UpdateAsync(Guid id, FlashSalesPlanUpdateDto input) |
|||
{ |
|||
return Service.UpdateAsync(id, input); |
|||
} |
|||
|
|||
[HttpDelete("{id}")] |
|||
public virtual Task DeleteAsync(Guid id) |
|||
{ |
|||
return Service.DeleteAsync(id); |
|||
} |
|||
|
|||
[HttpPost("{id}/pre-order")] |
|||
public virtual Task PreOrderAsync(Guid id) |
|||
{ |
|||
return Service.PreOrderAsync(id); |
|||
} |
|||
|
|||
[HttpGet("{id}/check-pre-order")] |
|||
public virtual Task CheckPreOrderAsync(Guid id) |
|||
{ |
|||
return Service.CheckPreOrderAsync(id); |
|||
} |
|||
|
|||
[HttpPost("{id}/order")] |
|||
public virtual Task CreateOrderAsync(Guid id, CreateOrderInput input) |
|||
{ |
|||
return Service.CreateOrderAsync(id, input); |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults.Dtos; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults; |
|||
|
|||
[Area(EShopPluginsFlashSalesRemoteServiceConsts.ModuleName)] |
|||
[RemoteService(Name = EShopPluginsFlashSalesRemoteServiceConsts.RemoteServiceName)] |
|||
[Route("/api/e-shop/plugins/flash-sales/flash-sales-result")] |
|||
public class FlashSalesResultController : FlashSalesController, IFlashSalesResultAppService |
|||
{ |
|||
protected IFlashSalesResultAppService Service { get; } |
|||
|
|||
public FlashSalesResultController(IFlashSalesResultAppService service) |
|||
{ |
|||
Service = service; |
|||
} |
|||
|
|||
[HttpGet("{id}")] |
|||
public virtual Task<FlashSalesResultDto> GetAsync(Guid id) |
|||
{ |
|||
return Service.GetAsync(id); |
|||
} |
|||
|
|||
[HttpGet] |
|||
public virtual Task<PagedResultDto<FlashSalesResultDto>> GetListAsync(FlashSalesResultGetListInput input) |
|||
{ |
|||
return Service.GetListAsync(input); |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System; |
|||
using EasyAbp.EShop.Plugins.FlashSales.MongoDB; |
|||
using Volo.Abp.Domain.Repositories.MongoDB; |
|||
using Volo.Abp.MongoDB; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
|
|||
public class MongoFlashSalesPlanRepository : MongoDbRepository<IFlashSalesMongoDbContext, FlashSalesPlan, Guid>, IFlashSalesPlanRepository |
|||
{ |
|||
public MongoFlashSalesPlanRepository(IMongoDbContextProvider<IFlashSalesMongoDbContext> dbContextProvider) : base(dbContextProvider) |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System; |
|||
using EasyAbp.EShop.Plugins.FlashSales.MongoDB; |
|||
using Volo.Abp.Domain.Repositories.MongoDB; |
|||
using Volo.Abp.MongoDB; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults; |
|||
|
|||
public class MongoFlashSalesResultRepository : MongoDbRepository<IFlashSalesMongoDbContext, FlashSalesResult, Guid>, IFlashSalesResultRepository |
|||
{ |
|||
public MongoFlashSalesResultRepository(IMongoDbContextProvider<IFlashSalesMongoDbContext> dbContextProvider) : base(dbContextProvider) |
|||
{ |
|||
} |
|||
} |
|||
@ -1,10 +1,12 @@ |
|||
namespace EasyAbp.EShop.Plugins.FlashSales.Web.Menus; |
|||
namespace EasyAbp.EShop.Plugins.FlashSales.Web.Menus; |
|||
|
|||
public class FlashSalesMenus |
|||
public static class FlashSalesMenus |
|||
{ |
|||
public const string Prefix = "FlashSales"; |
|||
public const string ModuleGroupPrefix = "EasyAbp.EShop"; |
|||
|
|||
//Add your menu items here...
|
|||
//public const string Home = Prefix + ".MyNewMenuItem";
|
|||
public const string Prefix = ModuleGroupPrefix + ".Plugins.FlashSales"; |
|||
|
|||
public const string FlashSalesPlan = Prefix + ".FlashSalesPlan"; |
|||
|
|||
public const string FlashSalesResult = Prefix + ".FlashSalesResult"; |
|||
} |
|||
|
|||
@ -0,0 +1,21 @@ |
|||
@page |
|||
@using Microsoft.AspNetCore.Mvc.Localization |
|||
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal |
|||
@using EasyAbp.EShop.Plugins.FlashSales.Localization |
|||
@using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalesPlans.FlashSalesPlan |
|||
|
|||
@inject IHtmlLocalizer<FlashSalesResource> L |
|||
@model CreateModalModel |
|||
@{ |
|||
Layout = null; |
|||
} |
|||
|
|||
<abp-dynamic-form abp-model="ViewModel" data-ajaxForm="true" asp-page="CreateModal"> |
|||
<abp-modal> |
|||
<abp-modal-header title="@L["CreateFlashSalesPlan"].Value"></abp-modal-header> |
|||
<abp-modal-body> |
|||
<abp-form-content /> |
|||
</abp-modal-body> |
|||
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer> |
|||
</abp-modal> |
|||
</abp-dynamic-form> |
|||
@ -0,0 +1,27 @@ |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos; |
|||
using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalesPlans.FlashSalesPlan.ViewModels; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalesPlans.FlashSalesPlan; |
|||
|
|||
public class CreateModalModel : FlashSalesPageModel |
|||
{ |
|||
[BindProperty] |
|||
public CreateFlashSalesPlanViewModel ViewModel { get; set; } |
|||
|
|||
protected IFlashSalesPlanAppService Service { get; } |
|||
|
|||
public CreateModalModel(IFlashSalesPlanAppService service) |
|||
{ |
|||
Service = service; |
|||
} |
|||
|
|||
public virtual async Task<IActionResult> OnPostAsync() |
|||
{ |
|||
var dto = ObjectMapper.Map<CreateFlashSalesPlanViewModel, FlashSalesPlanCreateDto>(ViewModel); |
|||
await Service.CreateAsync(dto); |
|||
return NoContent(); |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
@page |
|||
@using EasyAbp.EShop.Plugins.FlashSales.Localization |
|||
@using Microsoft.AspNetCore.Mvc.Localization |
|||
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal |
|||
@using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalesPlans.FlashSalesPlan |
|||
|
|||
@inject IHtmlLocalizer<FlashSalesResource> L |
|||
@model EditModalModel |
|||
@{ |
|||
Layout = null; |
|||
} |
|||
|
|||
<abp-dynamic-form abp-model="ViewModel" data-ajaxForm="true" asp-page="EditModal"> |
|||
<abp-modal> |
|||
<abp-modal-header title="@L["EditFlashSalesPlan"].Value"></abp-modal-header> |
|||
<abp-modal-body> |
|||
<abp-input asp-for="Id" /> |
|||
<abp-form-content /> |
|||
</abp-modal-body> |
|||
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer> |
|||
</abp-modal> |
|||
</abp-dynamic-form> |
|||
@ -0,0 +1,38 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos; |
|||
using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalesPlans.FlashSalesPlan.ViewModels; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalesPlans.FlashSalesPlan; |
|||
|
|||
public class EditModalModel : FlashSalesPageModel |
|||
{ |
|||
[HiddenInput] |
|||
[BindProperty(SupportsGet = true)] |
|||
public Guid Id { get; set; } |
|||
|
|||
[BindProperty] |
|||
public EditFlashSalesPlanViewModel ViewModel { get; set; } |
|||
|
|||
protected IFlashSalesPlanAppService Service { get; } |
|||
|
|||
public EditModalModel(IFlashSalesPlanAppService service) |
|||
{ |
|||
Service = service; |
|||
} |
|||
|
|||
public virtual async Task OnGetAsync() |
|||
{ |
|||
var dto = await Service.GetAsync(Id); |
|||
ViewModel = ObjectMapper.Map<FlashSalesPlanDto, EditFlashSalesPlanViewModel>(dto); |
|||
} |
|||
|
|||
public virtual async Task<IActionResult> OnPostAsync() |
|||
{ |
|||
var dto = ObjectMapper.Map<EditFlashSalesPlanViewModel, FlashSalesPlanUpdateDto>(ViewModel); |
|||
await Service.UpdateAsync(Id, dto); |
|||
return NoContent(); |
|||
} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
@page |
|||
@using EasyAbp.EShop.Plugins.FlashSales.Permissions |
|||
@using Microsoft.AspNetCore.Authorization |
|||
@using Microsoft.AspNetCore.Mvc.Localization |
|||
@using Volo.Abp.AspNetCore.Mvc.UI.Layout |
|||
@using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalesPlans.FlashSalesPlan |
|||
@using EasyAbp.EShop.Plugins.FlashSales.Localization |
|||
@using EasyAbp.EShop.Plugins.FlashSales.Web.Menus |
|||
|
|||
@model IndexModel |
|||
|
|||
@inject IPageLayout PageLayout |
|||
@inject IHtmlLocalizer<FlashSalesResource> L |
|||
@inject IAuthorizationService Authorization |
|||
@{ |
|||
PageLayout.Content.Title = L["FlashSalesPlan"].Value; |
|||
PageLayout.Content.BreadCrumb.Add(L["Menu:FlashSalesPlan"].Value); |
|||
PageLayout.Content.MenuItemName = FlashSalesMenus.FlashSalesPlan; |
|||
} |
|||
|
|||
@section scripts |
|||
{ |
|||
<abp-script src="/Pages/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlan/index.js" /> |
|||
} |
|||
@section styles |
|||
{ |
|||
<abp-style src="/Pages/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlan/index.css" /> |
|||
} |
|||
|
|||
<abp-card> |
|||
<abp-card-header> |
|||
<abp-row> |
|||
<abp-column size-md="_6"> |
|||
<abp-card-title>@L["FlashSalesPlan"]</abp-card-title> |
|||
</abp-column> |
|||
<abp-column size-md="_6" class="text-end"> |
|||
@if (await Authorization.IsGrantedAsync(FlashSalesPermissions.FlashSalesPlan.Create)) |
|||
{ |
|||
<abp-button id="NewFlashSalesPlanButton" |
|||
text="@L["CreateFlashSalesPlan"].Value" |
|||
icon="plus" |
|||
button-type="Primary" /> |
|||
} |
|||
</abp-column> |
|||
</abp-row> |
|||
</abp-card-header> |
|||
<abp-card-body> |
|||
<abp-table striped-rows="true" id="FlashSalesPlanTable" class="nowrap" /> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
@ -0,0 +1,11 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalesPlans.FlashSalesPlan; |
|||
|
|||
public class IndexModel : FlashSalesPageModel |
|||
{ |
|||
public virtual async Task OnGetAsync() |
|||
{ |
|||
await Task.CompletedTask; |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using System; |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalesPlans.FlashSalesPlan.ViewModels; |
|||
|
|||
public class CreateFlashSalesPlanViewModel |
|||
{ |
|||
[Display(Name = "FlashSalesPlanStoreId")] |
|||
public Guid StoreId { get; set; } |
|||
|
|||
[Display(Name = "FlashSalesPlanBeginTime")] |
|||
public DateTime BeginTime { get; set; } |
|||
|
|||
[Display(Name = "FlashSalesPlanEndTime")] |
|||
public DateTime EndTime { get; set; } |
|||
|
|||
[Display(Name = "FlashSalesPlanProductId")] |
|||
public Guid ProductId { get; set; } |
|||
|
|||
[Display(Name = "FlashSalesPlanProductSkuId")] |
|||
public Guid ProductSkuId { get; set; } |
|||
|
|||
[Display(Name = "FlashSalesPlanIsPublished")] |
|||
public bool IsPublished { get; set; } |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
using System; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; |
|||
using Volo.Abp.Domain.Entities; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalesPlans.FlashSalesPlan.ViewModels; |
|||
|
|||
public class EditFlashSalesPlanViewModel : IHasConcurrencyStamp |
|||
{ |
|||
[DisabledInput] |
|||
[ReadOnlyInput] |
|||
[Display(Name = "FlashSalesPlanStoreId")] |
|||
public Guid StoreId { get; set; } |
|||
|
|||
[Display(Name = "FlashSalesPlanBeginTime")] |
|||
public DateTime BeginTime { get; set; } |
|||
|
|||
[Display(Name = "FlashSalesPlanEndTime")] |
|||
public DateTime EndTime { get; set; } |
|||
|
|||
[Display(Name = "FlashSalesPlanProductId")] |
|||
public Guid ProductId { get; set; } |
|||
|
|||
[Display(Name = "FlashSalesPlanProductSkuId")] |
|||
public Guid ProductSkuId { get; set; } |
|||
|
|||
[Display(Name = "FlashSalesPlanIsPublished")] |
|||
public bool IsPublished { get; set; } |
|||
|
|||
[HiddenInput] |
|||
public string ConcurrencyStamp { get; set; } |
|||
} |
|||
@ -0,0 +1,87 @@ |
|||
$(function () { |
|||
|
|||
var l = abp.localization.getResource('EasyAbpEShopPluginsFlashSales'); |
|||
|
|||
var service = easyAbp.eShop.plugins.flashSales.flashSalesPlans.flashSalesPlan; |
|||
var createModal = new abp.ModalManager(abp.appPath + 'EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlan/CreateModal'); |
|||
var editModal = new abp.ModalManager(abp.appPath + 'EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlan/EditModal'); |
|||
|
|||
var dataTable = $('#FlashSalesPlanTable').DataTable(abp.libs.datatables.normalizeConfiguration({ |
|||
processing: true, |
|||
serverSide: true, |
|||
paging: true, |
|||
searching: false, |
|||
autoWidth: false, |
|||
scrollCollapse: true, |
|||
order: [[2, "asc"]], |
|||
ajax: abp.libs.datatables.createAjax(service.getList), |
|||
columnDefs: [ |
|||
{ |
|||
rowAction: { |
|||
items: |
|||
[ |
|||
{ |
|||
text: l('Edit'), |
|||
visible: abp.auth.isGranted('EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlan.Update'), |
|||
action: function (data) { |
|||
editModal.open({ id: data.record.id }); |
|||
} |
|||
}, |
|||
{ |
|||
text: l('Delete'), |
|||
visible: abp.auth.isGranted('EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlan.Delete'), |
|||
confirmMessage: function (data) { |
|||
return l('FlashSalesPlanDeletionConfirmationMessage', data.record.id); |
|||
}, |
|||
action: function (data) { |
|||
service.delete(data.record.id) |
|||
.then(function () { |
|||
abp.notify.info(l('SuccessfullyDeleted')); |
|||
dataTable.ajax.reload(); |
|||
}); |
|||
} |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
title: l('FlashSalesPlanStoreId'), |
|||
data: "storeId" |
|||
}, |
|||
{ |
|||
title: l('FlashSalesPlanBeginTime'), |
|||
data: "beginTime" |
|||
}, |
|||
{ |
|||
title: l('FlashSalesPlanEndTime'), |
|||
data: "endTime" |
|||
}, |
|||
{ |
|||
title: l('FlashSalesPlanProductId'), |
|||
data: "productId" |
|||
}, |
|||
{ |
|||
title: l('FlashSalesPlanProductSkuId'), |
|||
data: "productSkuId" |
|||
}, |
|||
{ |
|||
title: l('FlashSalesPlanIsPublished'), |
|||
data: "isPublished", |
|||
dataFormat: 'boolean' |
|||
}, |
|||
] |
|||
})); |
|||
|
|||
createModal.onResult(function () { |
|||
dataTable.ajax.reload(); |
|||
}); |
|||
|
|||
editModal.onResult(function () { |
|||
dataTable.ajax.reload(); |
|||
}); |
|||
|
|||
$('#NewFlashSalesPlanButton').click(function (e) { |
|||
e.preventDefault(); |
|||
createModal.open(); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,41 @@ |
|||
@page |
|||
@using EasyAbp.EShop.Plugins.FlashSales.Permissions |
|||
@using Microsoft.AspNetCore.Authorization |
|||
@using Microsoft.AspNetCore.Mvc.Localization |
|||
@using Volo.Abp.AspNetCore.Mvc.UI.Layout |
|||
@using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalesResults.FlashSalesResult |
|||
@using EasyAbp.EShop.Plugins.FlashSales.Localization |
|||
@using EasyAbp.EShop.Plugins.FlashSales.Web.Menus |
|||
|
|||
@model IndexModel |
|||
|
|||
@inject IPageLayout PageLayout |
|||
@inject IHtmlLocalizer<FlashSalesResource> L |
|||
@inject IAuthorizationService Authorization |
|||
@{ |
|||
PageLayout.Content.Title = L["FlashSalesResult"].Value; |
|||
PageLayout.Content.BreadCrumb.Add(L["Menu:FlashSalesResult"].Value); |
|||
PageLayout.Content.MenuItemName = FlashSalesMenus.FlashSalesResult; |
|||
} |
|||
|
|||
@section scripts |
|||
{ |
|||
<abp-script src="/Pages/EShop/Plugins/FlashSales/FlashSalesResults/FlashSalesResult/index.js" /> |
|||
} |
|||
@section styles |
|||
{ |
|||
<abp-style src="/Pages/EShop/Plugins/FlashSales/FlashSalesResults/FlashSalesResult/index.css" /> |
|||
} |
|||
|
|||
<abp-card> |
|||
<abp-card-header> |
|||
<abp-row> |
|||
<abp-column size-md="_6"> |
|||
<abp-card-title>@L["FlashSalesResult"]</abp-card-title> |
|||
</abp-column> |
|||
</abp-row> |
|||
</abp-card-header> |
|||
<abp-card-body> |
|||
<abp-table striped-rows="true" id="FlashSalesResultTable" class="nowrap" /> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
@ -0,0 +1,11 @@ |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.RazorPages; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalesResults.FlashSalesResult; |
|||
|
|||
public class IndexModel : PageModel |
|||
{ |
|||
public void OnGet() |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
@page |
|||
@using EasyAbp.EShop.Plugins.FlashSales.Localization |
|||
@using Microsoft.AspNetCore.Mvc.Localization |
|||
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal |
|||
@using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalesResults.FlashSalesResult |
|||
|
|||
@inject IHtmlLocalizer<FlashSalesResource> L |
|||
@model ViewModalModel |
|||
@{ |
|||
Layout = null; |
|||
} |
|||
|
|||
<abp-dynamic-form abp-model="ViewModel" data-ajaxForm="true" asp-page="ViewModal"> |
|||
<abp-modal> |
|||
<abp-modal-header title="@L["ViewFlashSalesResult"].Value"></abp-modal-header> |
|||
<abp-modal-body> |
|||
<abp-input asp-for="Id" /> |
|||
<abp-form-content /> |
|||
</abp-modal-body> |
|||
<abp-modal-footer buttons="@(AbpModalButtons.Close)"></abp-modal-footer> |
|||
</abp-modal> |
|||
</abp-dynamic-form> |
|||
@ -0,0 +1,31 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults.Dtos; |
|||
using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalesResults.FlashSalesResult.ViewModels; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalesResults.FlashSalesResult; |
|||
|
|||
public class ViewModalModel : FlashSalesPageModel |
|||
{ |
|||
[HiddenInput] |
|||
[BindProperty(SupportsGet = true)] |
|||
public Guid Id { get; set; } |
|||
|
|||
[BindProperty] |
|||
public ViewFlashSalesResultViewModel ViewModel { get; set; } |
|||
|
|||
protected IFlashSalesResultAppService Service { get; } |
|||
|
|||
public ViewModalModel(IFlashSalesResultAppService service) |
|||
{ |
|||
Service = service; |
|||
} |
|||
|
|||
public virtual async Task OnGetAsync() |
|||
{ |
|||
var dto = await Service.GetAsync(Id); |
|||
ViewModel = ObjectMapper.Map<FlashSalesResultDto, ViewFlashSalesResultViewModel>(dto); |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
using System; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalesResults.FlashSalesResult.ViewModels; |
|||
|
|||
public class ViewFlashSalesResultViewModel |
|||
{ |
|||
[Display(Name = "FlashSalesResultStoreId")] |
|||
public virtual Guid StoreId { get; protected set; } |
|||
|
|||
[Display(Name = "FlashSalesResultPlanId")] |
|||
public virtual Guid PlanId { get; protected set; } |
|||
|
|||
[Display(Name = "FlashSalesResultStatus")] |
|||
public virtual FlashSalesResultStatus Status { get; protected set; } |
|||
|
|||
[Display(Name = "FlashSalesResultUserId")] |
|||
public virtual Guid UserId { get; protected set; } |
|||
|
|||
[Display(Name = "FlashSalesResultOrderId")] |
|||
public virtual Guid? OrderId { get; protected set; } |
|||
} |
|||
@ -0,0 +1,62 @@ |
|||
$(function () { |
|||
|
|||
var l = abp.localization.getResource('EasyAbpEShopPluginsFlashSales'); |
|||
|
|||
var service = easyAbp.eShop.plugins.flashSales.flashSalesPlans.flashSalesPlan; |
|||
var viewModal = new abp.ModalManager(abp.appPath + 'EShop/Plugins/FlashSales/FlashSalesResults/FlashSalesResult/ViewModal'); |
|||
|
|||
var dataTable = $('#FlashSalesResultTable').DataTable(abp.libs.datatables.normalizeConfiguration({ |
|||
processing: true, |
|||
serverSide: true, |
|||
paging: true, |
|||
searching: false, |
|||
autoWidth: false, |
|||
scrollCollapse: true, |
|||
order: [[6, "desc"]], |
|||
ajax: abp.libs.datatables.createAjax(service.getList), |
|||
columnDefs: [ |
|||
{ |
|||
rowAction: { |
|||
items: |
|||
[ |
|||
{ |
|||
text: l('ViewFlashSalesPlan'), |
|||
action: function (data) { |
|||
viewModal.open({ id: data.record.id }); |
|||
} |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
title: l('FlashSalesResultStoreId'), |
|||
data: "storeId" |
|||
}, |
|||
{ |
|||
title: l('FlashSalesResultPlanId'), |
|||
data: "planId" |
|||
}, |
|||
{ |
|||
title: l('FlashSalesResultStatus'), |
|||
data: "status" |
|||
}, |
|||
{ |
|||
title: l('FlashSalesResultUserId'), |
|||
data: "userId" |
|||
}, |
|||
{ |
|||
title: l('FlashSalesResultOrderId'), |
|||
data: "orderId" |
|||
}, |
|||
{ |
|||
title: l('FlashSalesResultCreationTime'), |
|||
data: "creationTime", |
|||
dataFormat: 'datetime' |
|||
}, |
|||
] |
|||
})); |
|||
|
|||
viewModal.onResult(function () { |
|||
dataTable.ajax.reload(); |
|||
}); |
|||
}); |
|||
@ -1,11 +1,12 @@ |
|||
@page |
|||
@using Microsoft.Extensions.Localization |
|||
@using EasyAbp.EShop.Plugins.FlashSales.Localization |
|||
@using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EasyAbp.EShop.Plugins.FlashSales |
|||
@using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales |
|||
|
|||
@model IndexModel |
|||
@inject IStringLocalizer<FlashSalesResource> L |
|||
|
|||
@{ |
|||
} |
|||
|
|||
<h1>FlashSales</h1> |
|||
<p>@L["SamplePageMessage"]</p> |
|||
@ -1,4 +1,4 @@ |
|||
namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EasyAbp.EShop.Plugins.FlashSales; |
|||
namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales; |
|||
|
|||
public class IndexModel : FlashSalesPageModel |
|||
{ |
|||
Loading…
Reference in new issue