feat(FaqInActions) , feat(FaqManagementPage) , feat(FaqController)

subProduct
Amir Hossein Khademi 2024-08-07 16:15:53 +03:30
parent 5524cb4e43
commit 19f1f29548
45 changed files with 588 additions and 264 deletions

View File

@ -0,0 +1,56 @@
namespace Netina.Api.Controllers;
public class FaqController : ICarterModule
{
public void AddRoutes(IEndpointRouteBuilder app)
{
var group = app.NewVersionedApi("Faq")
.MapGroup("api/faq");
group.MapGet("/slug", GetFaqBySlugAsync)
.WithDisplayName("GetFaqBySlug")
.WithDescription("Get faq by slug , you have to send page slug")
.HasApiVersion(1.0);
group.MapGet("", GetFaqsAsync)
.WithDisplayName("GetFaqs")
.WithDescription("Get All Faqs ")
.RequireAuthorization(builder => builder.AddAuthenticationSchemes("Bearer")
.RequireAuthenticatedUser().RequireClaim(CustomClaimType.Permission, ApplicationPermission.ManageFaq))
.HasApiVersion(1.0);
group.MapPost("", CreateFaqAsync)
.WithDisplayName("Create Faq")
.WithDescription("Create Faq , you can create new faq or create update your faq ")
.RequireAuthorization(builder => builder.AddAuthenticationSchemes("Bearer")
.RequireAuthenticatedUser().RequireClaim(CustomClaimType.Permission, ApplicationPermission.ManageFaq))
.HasApiVersion(1.0);
group.MapPut("", UpdateFaqAsync)
.WithDisplayName("Update FaqAsync")
.WithDescription("Update Faq , you can create new faq or create update your faq ")
.RequireAuthorization(builder => builder.AddAuthenticationSchemes("Bearer")
.RequireAuthenticatedUser().RequireClaim(CustomClaimType.Permission, ApplicationPermission.ManageFaq))
.HasApiVersion(1.0);
group.MapDelete("{id}", DeleteFaqAsync)
.WithDisplayName("DeleteFaq")
.RequireAuthorization(builder => builder.AddAuthenticationSchemes("Bearer").RequireAuthenticatedUser().RequireClaim(CustomClaimType.Permission, ApplicationPermission.ManageFaq))
.HasApiVersion(1.0);
}
private async Task<IResult> DeleteFaqAsync([FromRoute]Guid id,[FromServices] IMediator mediator, CancellationToken cancellationToken)
=> TypedResults.Ok(await mediator.Send(new DeleteFaqCommand(Id:id), cancellationToken));
private async Task<IResult> GetFaqsAsync([FromQuery]int page, [FromQuery] int? count ,[FromServices] IMediator mediator, CancellationToken cancellationToken)
=> TypedResults.Ok(await mediator.Send(new GetFaqsQuery(Count: count ?? 0 , Page:page), cancellationToken));
private async Task<IResult> CreateFaqAsync([FromBody] CreateFaqCommand request,[FromServices]IMediator mediator , CancellationToken cancellationToken)
=> TypedResults.Ok(await mediator.Send(request, cancellationToken));
private async Task<IResult> UpdateFaqAsync([FromBody] UpdateFaqCommand request, [FromServices] IMediator mediator, CancellationToken cancellationToken)
=> TypedResults.Ok(await mediator.Send(request, cancellationToken));
private async Task<IResult> GetFaqBySlugAsync([FromQuery] string slug, [FromServices] IMediator mediator, CancellationToken cancellationToken)
=> TypedResults.Ok(await mediator.Send(new GetFaqQuery(null, slug), cancellationToken));
}

View File

@ -78,13 +78,14 @@ public class SeedController : ICarterModule
var baseCat = await mediator.Send(new CreateProductCategoryCommand("دسته بندی نشده", "محصولات دسته بندی نشده",
true,
default,
new List<StorageFileSDto>()),cancellationToken);
new List<StorageFileSDto>(),
new Dictionary<string, string>(),
new Dictionary<string, string>()),cancellationToken);
categories.Add(0,baseCat);
foreach (var requestDto in request)
{
var lDto = await mediator.Send(new CreateProductCategoryCommand(requestDto.Name,requestDto.Description,true,default,
new List<StorageFileSDto>()), cancellationToken);
new List<StorageFileSDto>(),new Dictionary<string, string>(),new Dictionary<string, string>()), cancellationToken);
categories.Add(requestDto.BaseCategoryId,lDto);
}
@ -97,13 +98,18 @@ public class SeedController : ICarterModule
if (key != "kKAYskyG8xPxKnJrHkuYxub4Ao2bnz7AOmNtwDT0RaqzaG7ZvbvaP29tCrC8wJ823RczJFXOIQT2bDOec4F38A==")
throw new AppException("Key is not valid", ApiResultStatusCode.UnAuthorized);
Dictionary<int, Guid> brands = new Dictionary<int, Guid>();
var baseBrand = await mediator.Send(new CreateBrandCommand("بدون برند","NoBrand", "محصولات بدون برند", false,string.Empty,
new List<StorageFileSDto>()), cancellationToken);
var baseBrand = await mediator.Send(new CreateBrandCommand("بدون برند","NoBrand",
"محصولات بدون برند",
false,
string.Empty,
new List<StorageFileSDto>(),
new Dictionary<string, string>(),
new Dictionary<string, string>()), cancellationToken);
brands.Add(0, baseBrand);
foreach (var requestDto in request)
{
var sDto = await mediator.Send(new CreateBrandCommand(requestDto.Name,string.Empty, requestDto.Description, false,
string.Empty, new List<StorageFileSDto>()), cancellationToken);
string.Empty, new List<StorageFileSDto>(),new Dictionary<string, string>(),new Dictionary<string, string>()), cancellationToken);
brands.Add(requestDto.BaseBrandId,sDto);
}

View File

@ -39,7 +39,9 @@ public class ExceptionHandlerMiddleware
catch (BaseApiException exception)
{
_logger.LogError(exception, exception.Message);
httpStatusCode = exception.HttpStatusCode;
httpStatusCode = exception.ApiStatusCode == ApiResultStatusCode.NotFound ? HttpStatusCode.NotFound :
exception.ApiStatusCode == ApiResultStatusCode.BadRequest ?
HttpStatusCode.BadRequest : exception.HttpStatusCode;
apiStatusCode = exception.ApiStatusCode;
if (_env.IsDevelopment())

View File

@ -1,8 +1,23 @@
namespace Netina.Domain.CommandQueries.Commands;
public sealed record CreateBrandCommand(string PersianName,string EnglishName, string Description , bool HasSpecialPage , string PageUrl, List<StorageFileSDto> Files) : IRequest<Guid>;
public sealed record CreateBrandCommand(string PersianName,
string EnglishName,
string Description ,
bool HasSpecialPage ,
string PageUrl,
List<StorageFileSDto> Files,
Dictionary<string, string> Faqs,
Dictionary<string, string> MetaTags) : IRequest<Guid>;
public sealed record UpdateBrandCommand(Guid Id,string PersianName, string EnglishName, string Description, bool HasSpecialPage, string PageUrl, List<StorageFileSDto> Files) : IRequest<bool>;
public sealed record UpdateBrandCommand(Guid Id,
string PersianName,
string EnglishName,
string Description,
bool HasSpecialPage,
string PageUrl,
List<StorageFileSDto> Files,
Dictionary<string, string> Faqs,
Dictionary<string, string> MetaTags) : IRequest<bool>;
public sealed record DeleteBrandCommand(Guid Id) : IRequest<bool>;

View File

@ -0,0 +1,15 @@
namespace Netina.Domain.CommandQueries.Commands;
public record CreateFaqCommand(
Dictionary<string, string> Faqs,
string Slug,
string Title) : IRequest<bool>;
public record DeleteFaqCommand(Guid Id) : IRequest<bool>;
public record UpdateFaqCommand(
Guid Id,
Dictionary<string, string> Faqs,
string Slug,
string Title) : IRequest<bool>;

View File

@ -5,7 +5,9 @@ public sealed record CreateProductCategoryCommand(
string Description,
bool IsMain,
Guid ParentId,
List<StorageFileSDto> Files) : IRequest<Guid>;
List<StorageFileSDto> Files,
Dictionary<string, string> Faqs,
Dictionary<string, string> MetaTags) : IRequest<Guid>;
public sealed record UpdateProductCategoryCommand(
Guid Id,
@ -13,6 +15,8 @@ public sealed record UpdateProductCategoryCommand(
string Description,
bool IsMain,
Guid ParentId,
List<StorageFileSDto> Files) : IRequest<bool>;
List<StorageFileSDto> Files,
Dictionary<string, string> Faqs,
Dictionary<string, string> MetaTags) : IRequest<bool>;
public sealed record DeleteProductCategoryCommand(Guid Id) : IRequest<bool>;

View File

@ -1,24 +1,26 @@
namespace Netina.Domain.CommandQueries.Commands;
public sealed record CreateProductCommand(
string PersianName,
string EnglishName,
string Summery,
string ExpertCheck,
string Tags,
string Warranty,
bool BeDisplayed,
double Cost,
double PackingCost,
int Stock,
bool HasExpressDelivery,
int MaxOrderCount,
bool IsSpecialOffer,
Guid BrandId,
Guid CategoryId,
DiscountSDto SpecialOffer,
List<SpecificationSDto> Specifications,
List<StorageFileSDto> Files):IRequest<ProductLDto>;
string PersianName,
string EnglishName,
string Summery,
string ExpertCheck,
string Tags,
string Warranty,
bool BeDisplayed,
double Cost,
double PackingCost,
int Stock,
bool HasExpressDelivery,
int MaxOrderCount,
bool IsSpecialOffer,
Guid BrandId,
Guid CategoryId,
DiscountSDto SpecialOffer,
List<SpecificationSDto> Specifications,
List<StorageFileSDto> Files,
Dictionary<string, string> Faqs,
Dictionary<string, string> MetaTags) : IRequest<ProductLDto>;
public sealed record UpdateProductCommand(
Guid Id,
@ -39,10 +41,13 @@ public sealed record UpdateProductCommand(
Guid CategoryId,
DiscountSDto SpecialOffer,
List<SpecificationSDto> Specifications,
List<StorageFileSDto> Files) : IRequest<bool>;
List<StorageFileSDto> Files,
Dictionary<string, string> Faqs,
Dictionary<string, string> MetaTags) : IRequest<bool>;
public sealed record ChangeProductDisplayedCommand(Guid Id,bool BeDisplayed) : IRequest<bool>;
public sealed record ChangeProductCostCommand(Guid Id,double Cost) : IRequest<bool>;
public sealed record ChangeProductDisplayedCommand(Guid Id, bool BeDisplayed) : IRequest<bool>;
public sealed record ChangeProductCostCommand(Guid Id, double Cost) : IRequest<bool>;
public sealed record DeleteProductCommand(Guid Id) : IRequest<bool>;

View File

@ -0,0 +1,4 @@
namespace Netina.Domain.CommandQueries.Queries;
public record GetFaqQuery(Guid? Id,string? Slug) : IRequest<BaseFaq>;
public record GetFaqsQuery(int Page , int Count = 0) : IRequest<List<BaseFaq>>;

View File

@ -6,6 +6,7 @@ public class BrandLDto : BaseDto<BrandLDto,Brand>
public string EnglishName { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public bool HasSpecialPage { get; set; }
public string Slug { get; set; } = string.Empty;
public string PageUrl { get; set; } = string.Empty;
public string HeaderFileName { get; set; } = string.Empty;
public List<StorageFileSDto> Files { get; internal set; } = new();

View File

@ -6,6 +6,7 @@ public class ProductCategoryLDto : BaseDto<ProductCategoryLDto, ProductCategory>
public string Description { get; set; } = string.Empty;
public Guid ParentId { get; set; }
public string ParentName { get; set; } = string.Empty;
public string Slug { get; set; } = string.Empty;
public bool IsMain { get; set; }
public List<ProductCategorySDto> Children { get; set; } = new();
public List<StorageFileSDto> Files { get; internal set; } = new();

View File

@ -5,6 +5,7 @@ public class BrandSDto : BaseDto<BrandSDto , Brand>
public string PersianName { get; set; } = string.Empty;
public string EnglishName { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string Slug { get; set; } = string.Empty;
public bool HasSpecialPage { get; set; }
public string PageUrl { get; set; } = string.Empty;
public string HeaderFileName { get; set; } = string.Empty;

View File

@ -1,10 +1,13 @@
namespace Netina.Domain.Entities.Brands;
using Microsoft.EntityFrameworkCore;
namespace Netina.Domain.Entities.Brands;
[AdaptTwoWays("[name]LDto", IgnoreAttributes = new[] { typeof(AdaptIgnoreAttribute) }, MapType = MapType.Map | MapType.MapToTarget | MapType.Projection)]
[AdaptTwoWays("[name]SDto", IgnoreAttributes = new[] { typeof(AdaptIgnoreAttribute) }, MapType = MapType.Map | MapType.MapToTarget)]
[AdaptTo("[name]SDto", IgnoreAttributes = new[] { typeof(AdaptIgnoreAttribute) }, MapType = MapType.Projection)]
[GenerateMapper]
[Index(nameof(Slug), IsUnique = true)]
public partial class Brand : ApiEntity
{
public Brand()

View File

@ -6,6 +6,9 @@
[AdaptTo("TorobProductResponseDto", IgnoreAttributes = new[] { typeof(AdaptIgnoreAttribute) }, MapType = MapType.Projection)]
//[AdaptTo("[name]SDto", IgnoreAttributes = new[] { typeof(AdaptIgnoreAttribute) }, MapType = MapType.Projection)]
[GenerateMapper]
[Index(nameof(Slug), IsUnique = true)]
public partial class Product : ApiEntity
{
public Product()

View File

@ -0,0 +1,13 @@
namespace Netina.Domain.Extensions;
public static class BrandExtension
{
public static string GetWebSiteUrl(this Brand product)
=> $"/brands/{product.Id}/{product.Slug}";
public static string GetWebSiteUrl(this BrandSDto product)
=> $"/brands/{product.Id}/{product.Slug}";
public static string GetWebSiteUrl(this BrandLDto product)
=> $"/brands/{product.Id}/{product.Slug}";
}

View File

@ -0,0 +1,13 @@
namespace Netina.Domain.Extensions;
public static class ProductCategoryExtension
{
public static string GetWebSiteUrl(this ProductCategory product)
=> $"/categories/{product.Id}/{product.Slug}";
public static string GetWebSiteUrl(this ProductCategorySDto product)
=> $"/categories/{product.Id}/{product.Slug}";
public static string GetWebSiteUrl(this ProductCategoryLDto product)
=> $"/categories/{product.Id}/{product.Slug}";
}

View File

@ -0,0 +1,13 @@
namespace Netina.Domain.Extensions;
public static class ProductExtension
{
public static string GetWebSiteUrl(this Product product)
=> $"/products/{product.Id}/{product.Slug}";
public static string GetWebSiteUrl(this ProductSDto product)
=> $"/products/{product.Id}/{product.Slug}";
public static string GetWebSiteUrl(this ProductLDto product)
=> $"/products/{product.Id}/{product.Slug}";
}

View File

@ -0,0 +1,8 @@
namespace Netina.Domain.MartenEntities.Faqs;
public class BaseFaq : MartenEntity
{
public Dictionary<string, string> Faqs { get; set; } = new();
public string Slug { get; set; } = string.Empty;
public string Title { get; set; } = string.Empty;
}

View File

@ -1,7 +0,0 @@
namespace Netina.Domain.MartenEntities.Pages;
[PageClassDisplay("FAQPage", "صفحه سوالات متداول")]
public class FAQPage
{
public Dictionary<string, string> Faqs { get; set; } = new Dictionary<string, string>();
}

View File

@ -11,6 +11,13 @@ public static class ApplicationClaims
Value = ApplicationPermission.ManageDashboard,
};
public static ClaimDto ManageFaq { get; } = new ClaimDto
{
Title = "مدیریت سوالات متداول",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ManageFaq,
};
public static ClaimDto ManageBlogs { get; } = new ClaimDto
{
Title = "مدیریت بلاگ ها",
@ -278,7 +285,8 @@ public static class ApplicationClaims
ManageUsers,
ViewUsers,
ManageFiles,
ViewFiles
ViewFiles,
ManageFaq
};
public static List<Claim> AllClaims = new List<Claim>
@ -319,7 +327,8 @@ public static class ApplicationClaims
ManageUsers.GetClaim,
ViewUsers.GetClaim,
ManageFiles.GetClaim,
ViewFiles.GetClaim
ViewFiles.GetClaim,
ManageFaq.GetClaim
};
public static List<Claim> CustomerClaims = new List<Claim>

View File

@ -2,6 +2,8 @@
public static class ApplicationPermission
{
public static string ManageFaq = nameof(ManageFaq);
public const string ViewSettings = nameof(ViewSettings);
public const string ManageSettings = nameof(ManageSettings);

View File

@ -12,6 +12,7 @@
<PackageReference Include="Mapster" Version="7.4.0" />
<PackageReference Include="Mapster.Core" Version="1.2.1" />
<PackageReference Include="MediatR" Version="12.4.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="8.0.7" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="8.0.7" />
<PackageReference Include="PropertyChanged.Fody" Version="4.1.0" />
</ItemGroup>
@ -59,6 +60,7 @@
<ItemGroup>
<Using Include="Mapster" />
<Using Include="MediatR" />
<Using Include="Microsoft.EntityFrameworkCore"/>
<Using Include="Microsoft.AspNetCore.Identity" />
<Using Include="Microsoft.Extensions.Logging" />
<Using Include="Netina.Common.Extensions" />
@ -78,6 +80,7 @@
<Using Include="Netina.Domain.Entities.Users" />
<Using Include="Netina.Domain.Entities.Warehouses" />
<Using Include="Netina.Domain.Enums" />
<Using Include="Netina.Domain.MartenEntities.Faqs" />
<Using Include="Netina.Domain.MartenEntities.Settings" />
<Using Include="System.ComponentModel.DataAnnotations" />
<Using Include="System.Diagnostics" />

View File

@ -1,33 +1,52 @@
using Marten;
using Netina.Repository.Abstracts;
namespace Netina.Infrastructure.Marten;
public class MartenRepository<TMartenEntity> : IMartenRepository<TMartenEntity> where TMartenEntity : IMartenEntity
public class MartenRepository<TMartenEntity>(IDocumentStore documentStore, ICurrentUserService currentUserService)
: IMartenRepository<TMartenEntity>
where TMartenEntity : IMartenEntity
{
private readonly IDocumentStore _documentStore;
public MartenRepository(IDocumentStore documentStore)
{
_documentStore = documentStore;
}
private readonly ICurrentUserService _currentUserService = currentUserService;
public async Task<List<TMartenEntity>> GetEntitiesAsync(CancellationToken cancellation)
{
await using var session = _documentStore.QuerySession();
var entities = await session.Query<TMartenEntity>().ToListAsync(cancellation);
await using var session = documentStore.QuerySession();
var entities = await session
.Query<TMartenEntity>()
.ToListAsync(cancellation);
return entities.ToList();
}
public async Task<List<TMartenEntity>> GetEntitiesAsync(int page, int count, CancellationToken cancellation)
{
await using var session = documentStore.QuerySession();
var entities = await session
.Query<TMartenEntity>()
.Skip(page * count)
.Take(count)
.ToListAsync(cancellation);
return entities.ToList();
}
public async Task<List<TMartenEntity>> GetEntitiesAsync(Expression<Func<TMartenEntity, bool>> expression, int page, int count, CancellationToken cancellation)
{
await using var session = documentStore.QuerySession();
var entities = await session.Query<TMartenEntity>().Where(expression)
.Skip(page * count)
.Take(count)
.ToListAsync(cancellation);
return entities.ToList();
}
public async Task<List<TMartenEntity>> GetEntitiesAsync(Expression<Func<TMartenEntity, bool>> expression, CancellationToken cancellation)
{
await using var session = _documentStore.QuerySession();
await using var session = documentStore.QuerySession();
var entities = await session.Query<TMartenEntity>().Where(expression).ToListAsync(cancellation);
return entities.ToList();
}
public async Task<TMartenEntity> GetEntityAsync(Guid id, CancellationToken cancellation)
{
await using var session = _documentStore.QuerySession();
await using var session = documentStore.QuerySession();
var setting = await session.LoadAsync<TMartenEntity>(id, cancellation);
if (setting == null)
throw new AppException($"{nameof(setting)} not found", ApiResultStatusCode.NotFound);
@ -36,7 +55,7 @@ public class MartenRepository<TMartenEntity> : IMartenRepository<TMartenEntity>
public async Task<TMartenEntity?> GetEntityAsync(Expression<Func<TMartenEntity, bool>> expression, CancellationToken cancellation)
{
await using var session = _documentStore.QuerySession();
await using var session = documentStore.QuerySession();
var entity = await session.Query<TMartenEntity>().FirstOrDefaultAsync(expression, cancellation);
return entity;
}
@ -46,16 +65,26 @@ public class MartenRepository<TMartenEntity> : IMartenRepository<TMartenEntity>
if (entity == null)
throw new AppException($"{nameof(entity)} is null", ApiResultStatusCode.BadRequest);
await using var session = _documentStore.LightweightSession();
await using var session = documentStore.LightweightSession();
session.Store(entity);
await session.SaveChangesAsync(cancellation);
}
public async Task UpdateEntityAsync(TMartenEntity entity, CancellationToken cancellation = default)
{
if (entity == null)
throw new AppException($"{nameof(entity)} is null", ApiResultStatusCode.BadRequest);
await using var session = documentStore.LightweightSession();
session.Update(entity);
await session.SaveChangesAsync(cancellation);
}
public async Task RemoveEntityAsync(TMartenEntity entity, CancellationToken cancellation)
{
if (entity == null)
throw new AppException($"{nameof(entity)} is null", ApiResultStatusCode.BadRequest);
await using var session = _documentStore.LightweightSession();
await using var session = documentStore.LightweightSession();
session.Delete(entity);
await session.SaveChangesAsync(cancellation);
}

View File

@ -1,16 +1,10 @@
using Marten;
using Netina.Repository.Abstracts;
namespace Netina.Infrastructure.Marten;
public class MartenRepositoryWrapper : IMartenRepositoryWrapper
public class MartenRepositoryWrapper(IDocumentStore documentStore,ICurrentUserService currentUserService) : IMartenRepositoryWrapper
{
private readonly IDocumentStore _documentStore;
public MartenRepositoryWrapper(IDocumentStore documentStore)
{
_documentStore = documentStore;
}
public IMartenRepository<TMartenEntity> SetRepository<TMartenEntity>() where TMartenEntity : IMartenEntity
=> new MartenRepository<TMartenEntity>(_documentStore);
=> new MartenRepository<TMartenEntity>(documentStore, currentUserService);
}

View File

@ -102,7 +102,7 @@ public class DigikalaScraper : IDigikalaScraper
newSummery,
string.Empty, string.Empty, string.Empty, true, 0,
0, 0, false
, 5, false, nonBrand.Id, nonCat.Id, new DiscountSDto(), specifications, files);
, 5, false, nonBrand.Id, nonCat.Id, new DiscountSDto(), specifications, files,new Dictionary<string, string>(),new Dictionary<string, string>());
await _mediator.Send(request, cancellationToken);
return true;

View File

@ -2,14 +2,8 @@
namespace Netina.Repository.Handlers.Brands;
public class CreateBrandCommandHandler : IRequestHandler<CreateBrandCommand , Guid>
public class CreateBrandCommandHandler(IRepositoryWrapper repositoryWrapper,IMartenRepositoryWrapper martenRepositoryWrapper , IMediator mediator) : IRequestHandler<CreateBrandCommand, Guid>
{
private readonly IRepositoryWrapper _repositoryWrapper;
public CreateBrandCommandHandler(IRepositoryWrapper repositoryWrapper)
{
_repositoryWrapper = repositoryWrapper;
}
public async Task<Guid> Handle(CreateBrandCommand request, CancellationToken cancellationToken)
{
var ent = Brand.Create(request.PersianName,request.EnglishName, request.Description, request.HasSpecialPage, request.PageUrl);
@ -17,8 +11,28 @@ public class CreateBrandCommandHandler : IRequestHandler<CreateBrandCommand , Gu
{
ent.AddFile(file.Name, file.FileLocation, file.FileName, file.IsHeader, file.IsPrimary, file.FileType);
}
_repositoryWrapper.SetRepository<Brand>().Add(ent);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
repositoryWrapper.SetRepository<Brand>().Add(ent);
await repositoryWrapper.SaveChangesAsync(cancellationToken);
await UpdateFaqAsync(ent, request.Faqs, cancellationToken);
return ent.Id;
}
private async Task UpdateFaqAsync(Brand newEnt, Dictionary<string, string> faqs, CancellationToken cancellationToken)
{
var url = newEnt.GetWebSiteUrl();
var oldFaq = await martenRepositoryWrapper.SetRepository<BaseFaq>()
.GetEntityAsync(f => f.Slug == newEnt.Slug, cancellationToken);
var newFaq = await martenRepositoryWrapper.SetRepository<BaseFaq>()
.GetEntityAsync(f => f.Slug == url, cancellationToken);
if (oldFaq != null)
await mediator.Send(new DeleteFaqCommand(oldFaq.Id), cancellationToken);
if (newFaq == null)
await mediator.Send(new CreateFaqCommand(faqs, url, newEnt.EnglishName), cancellationToken);
else
await mediator.Send(new UpdateFaqCommand(newFaq.Id, faqs, url, newEnt.EnglishName), cancellationToken);
}
}

View File

@ -1,19 +1,12 @@
using Microsoft.EntityFrameworkCore;
using Netina.Domain.Entities.Brands;
using Netina.Domain.Entities.Brands;
namespace Netina.Repository.Handlers.Brands;
public class UpdateBrandCommandHandler : IRequestHandler<UpdateBrandCommand,bool>
public class UpdateBrandCommandHandler(IRepositoryWrapper repositoryWrapper,IMartenRepositoryWrapper martenRepositoryWrapper,IMediator mediator) : IRequestHandler<UpdateBrandCommand, bool>
{
private readonly IRepositoryWrapper _repositoryWrapper;
public UpdateBrandCommandHandler(IRepositoryWrapper repositoryWrapper)
{
_repositoryWrapper = repositoryWrapper;
}
public async Task<bool> Handle(UpdateBrandCommand request, CancellationToken cancellationToken)
{
var ent = await _repositoryWrapper.SetRepository<Brand>().TableNoTracking
var ent = await repositoryWrapper.SetRepository<Brand>().TableNoTracking
.FirstOrDefaultAsync(b => b.Id == request.Id, cancellationToken);
if (ent == null)
throw new AppException("Brand not found");
@ -22,14 +15,14 @@ public class UpdateBrandCommandHandler : IRequestHandler<UpdateBrandCommand,bool
newEnt.CreatedAt = ent.CreatedAt;
newEnt.CreatedBy = ent.CreatedBy;
var dbFiles = await _repositoryWrapper.SetRepository<BrandStorageFile>().TableNoTracking
var dbFiles = await repositoryWrapper.SetRepository<BrandStorageFile>().TableNoTracking
.Where(s => s.BrandId == newEnt.Id).ToListAsync(cancellationToken);
foreach (var dbFile in dbFiles)
{
if (request.Files.FirstOrDefault(s => s.Id == dbFile.Id) == null)
{
_repositoryWrapper.SetRepository<BrandStorageFile>().Delete(dbFile);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
repositoryWrapper.SetRepository<BrandStorageFile>().Delete(dbFile);
await repositoryWrapper.SaveChangesAsync(cancellationToken);
}
}
foreach (var file in request.Files.Where(f => f.Id == default))
@ -37,8 +30,26 @@ public class UpdateBrandCommandHandler : IRequestHandler<UpdateBrandCommand,bool
newEnt.AddFile(file.Name, file.FileLocation, file.FileName, file.IsHeader, file.IsPrimary, file.FileType);
}
_repositoryWrapper.SetRepository<Brand>().Update(newEnt);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
repositoryWrapper.SetRepository<Brand>().Update(newEnt);
await repositoryWrapper.SaveChangesAsync(cancellationToken);
await UpdateFaqAsync(newEnt, request.Faqs, cancellationToken);
return true;
}
private async Task UpdateFaqAsync(Brand newEnt, Dictionary<string, string> faqs, CancellationToken cancellationToken)
{
var url = newEnt.GetWebSiteUrl();
var oldFaq = await martenRepositoryWrapper.SetRepository<BaseFaq>()
.GetEntityAsync(f => f.Slug == newEnt.Slug, cancellationToken);
var newFaq = await martenRepositoryWrapper.SetRepository<BaseFaq>()
.GetEntityAsync(f => f.Slug == url, cancellationToken);
if (oldFaq != null)
await mediator.Send(new DeleteFaqCommand(oldFaq.Id), cancellationToken);
if (newFaq == null)
await mediator.Send(new CreateFaqCommand(faqs, url, newEnt.EnglishName), cancellationToken);
else
await mediator.Send(new UpdateFaqCommand(newFaq.Id, faqs, url, newEnt.EnglishName), cancellationToken);
}
}

View File

@ -0,0 +1,18 @@
namespace Netina.Repository.Handlers.Faqs;
public class CreateFaqCommandHandler(IMartenRepositoryWrapper martenRepositoryWrapper)
: IRequestHandler<CreateFaqCommand, bool>
{
public async Task<bool> Handle(CreateFaqCommand request, CancellationToken cancellationToken)
{
var faq = new BaseFaq
{
Faqs = request.Faqs,
Slug = request.Slug,
Title = request.Title
};
await martenRepositoryWrapper.SetRepository<BaseFaq>()
.AddOrUpdateEntityAsync(faq, cancellationToken);
return true;
}
}

View File

@ -0,0 +1,12 @@
namespace Netina.Repository.Handlers.Faqs;
public class DeleteFaqCommandHandler(IMartenRepositoryWrapper martenRepositoryWrapper)
: IRequestHandler<DeleteFaqCommand, bool>
{
public async Task<bool> Handle(DeleteFaqCommand request, CancellationToken cancellationToken)
{
var ent = await martenRepositoryWrapper.SetRepository<BaseFaq>().GetEntityAsync(request.Id, cancellationToken);
await martenRepositoryWrapper.SetRepository<BaseFaq>().RemoveEntityAsync(ent, cancellationToken);
return true;
}
}

View File

@ -0,0 +1,27 @@
namespace Netina.Repository.Handlers.Faqs;
public class GetFaqQueryHandler(IMartenRepositoryWrapper martenRepositoryWrapper)
: IRequestHandler<GetFaqQuery, BaseFaq>
{
public async Task<BaseFaq> Handle(GetFaqQuery request, CancellationToken cancellationToken)
{
if (request.Id is not null)
{
var ent = await martenRepositoryWrapper.SetRepository<BaseFaq>()
.GetEntityAsync(request.Id.Value, cancellationToken);
if (ent == null)
return new BaseFaq();
return ent;
}else if (request.Slug != null)
{
var htmlSlug = request.Slug;
var ent = await martenRepositoryWrapper.SetRepository<BaseFaq>()
.GetEntityAsync(f=>f.Slug == htmlSlug, cancellationToken);
if (ent == null)
return new BaseFaq();
return ent;
}
return new BaseFaq();
}
}

View File

@ -0,0 +1,16 @@
namespace Netina.Repository.Handlers.Faqs;
public class GetFaqsQueryHandler(IMartenRepositoryWrapper martenRepositoryWrapper)
: IRequestHandler<GetFaqsQuery, List<BaseFaq>>
{
public async Task<List<BaseFaq>> Handle(GetFaqsQuery request, CancellationToken cancellationToken)
{
var count = request.Count > 0 ? request.Count : 20;
if (count > 50)
throw new BaseApiException(ApiResultStatusCode.BadRequest, "Count limit is 50");
var response = await martenRepositoryWrapper.SetRepository<BaseFaq>()
.GetEntitiesAsync(request.Page, count, cancellationToken);
response.ForEach(f => { f.Faqs ??= new Dictionary<string, string>(); });
return response;
}
}

View File

@ -0,0 +1,24 @@
namespace Netina.Repository.Handlers.Faqs;
public class UpdateFaqCommandHandler(IMartenRepositoryWrapper martenRepositoryWrapper)
: IRequestHandler<UpdateFaqCommand, bool>
{
public async Task<bool> Handle(UpdateFaqCommand request, CancellationToken cancellationToken)
{
var ent = await martenRepositoryWrapper.SetRepository<BaseFaq>()
.GetEntityAsync(request.Id, cancellationToken);
if (ent == null)
throw new BaseApiException(ApiResultStatusCode.NotFound, "Faq not found");
var newEnt = new BaseFaq
{
Id = ent.Id,
Faqs = request.Faqs,
Slug = request.Slug,
Title = request.Title
};
await martenRepositoryWrapper.SetRepository<BaseFaq>()
.UpdateEntityAsync(newEnt, cancellationToken);
return true;
}
}

View File

@ -1,14 +1,8 @@
namespace Netina.Repository.Handlers.ProductCategories;
public class CreateProductCategoryCommandHandler : IRequestHandler<CreateProductCategoryCommand,Guid>
public class CreateProductCategoryCommandHandler(IRepositoryWrapper repositoryWrapper,IMediator mediator,IMartenRepositoryWrapper martenRepositoryWrapper)
: IRequestHandler<CreateProductCategoryCommand, Guid>
{
private readonly IRepositoryWrapper _repositoryWrapper;
public CreateProductCategoryCommandHandler(IRepositoryWrapper repositoryWrapper)
{
_repositoryWrapper = repositoryWrapper;
}
public async Task<Guid> Handle(CreateProductCategoryCommand request, CancellationToken cancellationToken)
{
var ent = ProductCategory.Create(request.Name, request.Description, request.IsMain);
@ -18,8 +12,25 @@ public class CreateProductCategoryCommandHandler : IRequestHandler<CreateProduct
{
ent.AddFile(file.Name, file.FileLocation, file.FileName, file.IsHeader, file.IsPrimary, file.FileType);
}
_repositoryWrapper.SetRepository<ProductCategory>().Add(ent);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
repositoryWrapper.SetRepository<ProductCategory>().Add(ent);
await repositoryWrapper.SaveChangesAsync(cancellationToken);
await UpdateFaqAsync(ent, request.Faqs, cancellationToken);
return ent.Id;
}
private async Task UpdateFaqAsync(ProductCategory newEnt, Dictionary<string, string> faqs, CancellationToken cancellationToken)
{
var url = newEnt.GetWebSiteUrl();
var oldFaq = await martenRepositoryWrapper.SetRepository<BaseFaq>()
.GetEntityAsync(f => f.Slug == newEnt.Slug, cancellationToken);
var newFaq = await martenRepositoryWrapper.SetRepository<BaseFaq>()
.GetEntityAsync(f => f.Slug == url, cancellationToken);
if (oldFaq != null)
await mediator.Send(new DeleteFaqCommand(oldFaq.Id), cancellationToken);
if (newFaq == null)
await mediator.Send(new CreateFaqCommand(faqs, url, newEnt.Name), cancellationToken);
else
await mediator.Send(new UpdateFaqCommand(newFaq.Id, faqs, url, newEnt.Name), cancellationToken);
}
}

View File

@ -1,24 +1,17 @@
using Microsoft.EntityFrameworkCore;
namespace Netina.Repository.Handlers.ProductCategories;
namespace Netina.Repository.Handlers.ProductCategories;
public class DeleteProductCategoryCommandHandler : IRequestHandler<DeleteProductCategoryCommand, bool>
public class DeleteProductCategoryCommandHandler(IRepositoryWrapper repositoryWrapper)
: IRequestHandler<DeleteProductCategoryCommand, bool>
{
private readonly IRepositoryWrapper _repositoryWrapper;
public DeleteProductCategoryCommandHandler(IRepositoryWrapper repositoryWrapper)
{
_repositoryWrapper = repositoryWrapper;
}
public async Task<bool> Handle(DeleteProductCategoryCommand request, CancellationToken cancellationToken)
{
var ent = await _repositoryWrapper.SetRepository<ProductCategory>().TableNoTracking
var ent = await repositoryWrapper.SetRepository<ProductCategory>().TableNoTracking
.FirstOrDefaultAsync(c => c.Id == request.Id, cancellationToken);
if (ent == null)
throw new AppException("ProductCategory not found", ApiResultStatusCode.NotFound);
_repositoryWrapper.SetRepository<ProductCategory>().Delete(ent);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
repositoryWrapper.SetRepository<ProductCategory>().Delete(ent);
await repositoryWrapper.SaveChangesAsync(cancellationToken);
return true;
}
}

View File

@ -2,21 +2,16 @@
namespace Netina.Repository.Handlers.ProductCategories;
public class GetProductCategoriesQueryHandler : IRequestHandler<GetProductCategoriesQuery, List<ProductCategorySDto>>
public class GetProductCategoriesQueryHandler(IRepositoryWrapper repositoryWrapper)
: IRequestHandler<GetProductCategoriesQuery, List<ProductCategorySDto>>
{
private readonly IRepositoryWrapper _repositoryWrapper;
public GetProductCategoriesQueryHandler(IRepositoryWrapper repositoryWrapper)
{
_repositoryWrapper = repositoryWrapper;
}
public async Task<List<ProductCategorySDto>> Handle(GetProductCategoriesQuery request, CancellationToken cancellationToken)
{
IQueryable<ProductCategory> basCategories;
List<ProductCategorySDto> groupCats;
if (request.CategoryName != null)
{
basCategories = _repositoryWrapper.SetRepository<ProductCategory>()
basCategories = repositoryWrapper.SetRepository<ProductCategory>()
.TableNoTracking
.Where(c => c.Name.Trim().Contains(request.CategoryName.Trim()))
.OrderByDescending(c => c.CreatedAt);
@ -24,7 +19,7 @@ public class GetProductCategoriesQueryHandler : IRequestHandler<GetProductCatego
else
{
basCategories = _repositoryWrapper.SetRepository<ProductCategory>()
basCategories = repositoryWrapper.SetRepository<ProductCategory>()
.TableNoTracking
.OrderByDescending(c=>c.CreatedAt);
}

View File

@ -2,15 +2,9 @@
namespace Netina.Repository.Handlers.ProductCategories;
public class GetProductCategoryChildrenQueryHandler : IRequestHandler<GetProductCategoryChildrenQuery,List<Guid>>
public class GetProductCategoryChildrenQueryHandler(IRepositoryWrapper repositoryWrapper)
: IRequestHandler<GetProductCategoryChildrenQuery, List<Guid>>
{
private readonly IRepositoryWrapper _repositoryWrapper;
public GetProductCategoryChildrenQueryHandler(IRepositoryWrapper repositoryWrapper)
{
_repositoryWrapper = repositoryWrapper;
}
public async Task<List<Guid>> Handle(GetProductCategoryChildrenQuery request, CancellationToken cancellationToken)
{
if (request.Id == default)
@ -23,7 +17,7 @@ public class GetProductCategoryChildrenQueryHandler : IRequestHandler<GetProduct
private async Task<List<Guid>> Recursive(Guid id,CancellationToken cancellationToken)
{
var children = await _repositoryWrapper.SetRepository<ProductCategory>()
var children = await repositoryWrapper.SetRepository<ProductCategory>()
.TableNoTracking
.Where(c => c.ParentId == id)
.Select(c => c.Id)

View File

@ -2,17 +2,12 @@
namespace Netina.Repository.Handlers.ProductCategories;
public class GetProductCategoryQueryHandler : IRequestHandler<GetProductCategoryQuery, ProductCategoryLDto>
public class GetProductCategoryQueryHandler(IRepositoryWrapper repositoryWrapper)
: IRequestHandler<GetProductCategoryQuery, ProductCategoryLDto>
{
private readonly IRepositoryWrapper _repositoryWrapper;
public GetProductCategoryQueryHandler(IRepositoryWrapper repositoryWrapper)
{
_repositoryWrapper = repositoryWrapper;
}
public async Task<ProductCategoryLDto> Handle(GetProductCategoryQuery request, CancellationToken cancellationToken)
{
var ent = await _repositoryWrapper.SetRepository<ProductCategory>().TableNoTracking
var ent = await repositoryWrapper.SetRepository<ProductCategory>().TableNoTracking
.Where(b => b.Id == request.Id)
.Select(ProductCategoryMapper.ProjectToLDto)
.FirstOrDefaultAsync(cancellationToken);

View File

@ -1,18 +1,11 @@
using Microsoft.EntityFrameworkCore;
namespace Netina.Repository.Handlers.ProductCategories;
namespace Netina.Repository.Handlers.ProductCategories;
public class UpdateProductCategoryCommandHandler : IRequestHandler<UpdateProductCategoryCommand, bool>
public class UpdateProductCategoryCommandHandler(IRepositoryWrapper repositoryWrapper,IMediator mediator,IMartenRepositoryWrapper martenRepositoryWrapper)
: IRequestHandler<UpdateProductCategoryCommand, bool>
{
private readonly IRepositoryWrapper _repositoryWrapper;
public UpdateProductCategoryCommandHandler(IRepositoryWrapper repositoryWrapper)
{
_repositoryWrapper = repositoryWrapper;
}
public async Task<bool> Handle(UpdateProductCategoryCommand request, CancellationToken cancellationToken)
{
var ent = await _repositoryWrapper.SetRepository<ProductCategory>().TableNoTracking
var ent = await repositoryWrapper.SetRepository<ProductCategory>().TableNoTracking
.FirstOrDefaultAsync(c => c.Id == request.Id, cancellationToken);
if (ent == null)
throw new AppException("ProductCategory not found", ApiResultStatusCode.NotFound);
@ -25,22 +18,39 @@ public class UpdateProductCategoryCommandHandler : IRequestHandler<UpdateProduct
newEnt.SetParent(request.ParentId);
var dbFiles = await _repositoryWrapper.SetRepository<ProductCategoryStorageFile>().TableNoTracking
var dbFiles = await repositoryWrapper.SetRepository<ProductCategoryStorageFile>().TableNoTracking
.Where(s => s.CategoryId == newEnt.Id).ToListAsync(cancellationToken);
foreach (var dbFile in dbFiles)
{
if (request.Files.FirstOrDefault(s => s.Id == dbFile.Id) == null)
{
_repositoryWrapper.SetRepository<ProductCategoryStorageFile>().Delete(dbFile);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
repositoryWrapper.SetRepository<ProductCategoryStorageFile>().Delete(dbFile);
await repositoryWrapper.SaveChangesAsync(cancellationToken);
}
}
foreach (var file in request.Files.Where(f => f.Id == default))
{
newEnt.AddFile(file.Name, file.FileLocation, file.FileName, file.IsHeader, file.IsPrimary, file.FileType);
}
_repositoryWrapper.SetRepository<ProductCategory>().Update(newEnt);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
repositoryWrapper.SetRepository<ProductCategory>().Update(newEnt);
await repositoryWrapper.SaveChangesAsync(cancellationToken);
await UpdateFaqAsync(newEnt, request.Faqs, cancellationToken);
return true;
}
private async Task UpdateFaqAsync(ProductCategory newEnt, Dictionary<string, string> faqs, CancellationToken cancellationToken)
{
var url = newEnt.GetWebSiteUrl();
var oldFaq = await martenRepositoryWrapper.SetRepository<BaseFaq>()
.GetEntityAsync(f => f.Slug == newEnt.Slug, cancellationToken);
var newFaq = await martenRepositoryWrapper.SetRepository<BaseFaq>()
.GetEntityAsync(f => f.Slug == url, cancellationToken);
if (oldFaq != null)
await mediator.Send(new DeleteFaqCommand(oldFaq.Id), cancellationToken);
if (newFaq == null)
await mediator.Send(new CreateFaqCommand(faqs, url, newEnt.Name), cancellationToken);
else
await mediator.Send(new UpdateFaqCommand(newFaq.Id, faqs, url, newEnt.Name), cancellationToken);
}
}

View File

@ -1,16 +1,8 @@
namespace Netina.Repository.Handlers.Products;
public class CreateProductCommandHandler : IRequestHandler<CreateProductCommand, ProductLDto>
public class CreateProductCommandHandler(IRepositoryWrapper repositoryWrapper,IMartenRepositoryWrapper martenRepositoryWrapper, IMediator mediator)
: IRequestHandler<CreateProductCommand, ProductLDto>
{
private readonly IRepositoryWrapper _repositoryWrapper;
private readonly IMediator _mediator;
public CreateProductCommandHandler(IRepositoryWrapper repositoryWrapper,IMediator mediator)
{
_repositoryWrapper = repositoryWrapper;
_mediator = mediator;
}
public async Task<ProductLDto> Handle(CreateProductCommand request, CancellationToken cancellationToken)
{
@ -34,8 +26,8 @@ public class CreateProductCommandHandler : IRequestHandler<CreateProductCommand,
_repositoryWrapper.SetRepository<Product>().Add(ent);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
repositoryWrapper.SetRepository<Product>().Add(ent);
await repositoryWrapper.SaveChangesAsync(cancellationToken);
if (request.IsSpecialOffer)
{
@ -48,15 +40,34 @@ public class CreateProductCommandHandler : IRequestHandler<CreateProductCommand,
if (request.SpecialOffer.Id == default)
{
var discountRequest = discount.Adapt<CreateDiscountCommand>();
await _mediator.Send(discountRequest, cancellationToken);
await mediator.Send(discountRequest, cancellationToken);
}
else
{
var discountRequest = discount.Adapt<UpdateDiscountCommand>();
await _mediator.Send(discountRequest, cancellationToken);
await mediator.Send(discountRequest, cancellationToken);
}
}
await UpdateFaqAsync(ent, request.Faqs, cancellationToken);
return ent.AdaptToLDto();
}
private async Task UpdateFaqAsync(Product newEnt, Dictionary<string, string> faqs, CancellationToken cancellationToken)
{
var url = newEnt.GetWebSiteUrl();
var oldFaq = await martenRepositoryWrapper.SetRepository<BaseFaq>()
.GetEntityAsync(f => f.Slug == newEnt.Slug, cancellationToken);
var newFaq = await martenRepositoryWrapper.SetRepository<BaseFaq>()
.GetEntityAsync(f => f.Slug == url, cancellationToken);
if (oldFaq != null)
await mediator.Send(new DeleteFaqCommand(oldFaq.Id), cancellationToken);
if (newFaq == null)
await mediator.Send(new CreateFaqCommand(faqs, url, newEnt.EnglishName), cancellationToken);
else
await mediator.Send(new UpdateFaqCommand(newFaq.Id, faqs, url, newEnt.EnglishName), cancellationToken);
}
}

View File

@ -2,23 +2,18 @@
namespace Netina.Repository.Handlers.Products;
public class DeleteProductCommandHandler : IRequestHandler<DeleteProductCommand, bool>
public class DeleteProductCommandHandler(IRepositoryWrapper repositoryWrapper)
: IRequestHandler<DeleteProductCommand, bool>
{
private readonly IRepositoryWrapper _repositoryWrapper;
public DeleteProductCommandHandler(IRepositoryWrapper repositoryWrapper)
{
_repositoryWrapper = repositoryWrapper;
}
public async Task<bool> Handle(DeleteProductCommand request, CancellationToken cancellationToken)
{
var ent = await _repositoryWrapper.SetRepository<Product>().TableNoTracking
var ent = await repositoryWrapper.SetRepository<Product>().TableNoTracking
.FirstOrDefaultAsync(d => d.Id == request.Id, cancellationToken);
if (ent == null)
throw new AppException("Product NotFound", ApiResultStatusCode.NotFound);
_repositoryWrapper.SetRepository<Product>().Delete(ent);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
repositoryWrapper.SetRepository<Product>().Delete(ent);
await repositoryWrapper.SaveChangesAsync(cancellationToken);
return true;
}
}

View File

@ -1,6 +1,4 @@
using Microsoft.EntityFrameworkCore;
namespace Netina.Repository.Handlers.Products;
namespace Netina.Repository.Handlers.Products;
public class GetProductQueryHandler(IRepositoryWrapper repositoryWrapper, IMediator mediator)
: IRequestHandler<GetProductQuery, GetProductResponseDto>
@ -13,7 +11,7 @@ public class GetProductQueryHandler(IRepositoryWrapper repositoryWrapper, IMedia
.FirstOrDefaultAsync(cancellationToken);
if (ent == null)
throw new AppException("Product not found", ApiResultStatusCode.NotFound);
throw new BaseApiException(ApiResultStatusCode.NotFound,"Product not found");
await mediator.Send(new CalculateProductDiscountCommand(ent), cancellationToken);

View File

@ -2,25 +2,19 @@
namespace Netina.Repository.Handlers.Products;
public class GetProductsQueryHandler : IRequestHandler<GetProductsQuery, GetProductsResponseDto>
public class GetProductsQueryHandler(
IRepositoryWrapper repositoryWrapper,
IMediator mediator,
ICurrentUserService currentUserService)
: IRequestHandler<GetProductsQuery, GetProductsResponseDto>
{
private readonly IRepositoryWrapper _repositoryWrapper;
private readonly IMediator _mediator;
private readonly ICurrentUserService _currentUserService;
public GetProductsQueryHandler(IRepositoryWrapper repositoryWrapper, IMediator mediator, ICurrentUserService currentUserService)
{
_repositoryWrapper = repositoryWrapper;
_mediator = mediator;
_currentUserService = currentUserService;
}
public async Task<GetProductsResponseDto> Handle(GetProductsQuery request, CancellationToken cancellationToken)
{
var response = new GetProductsResponseDto();
var products = _repositoryWrapper.SetRepository<Product>().TableNoTracking;
if (_currentUserService.JwtToken == null)
var products = repositoryWrapper.SetRepository<Product>().TableNoTracking;
if (currentUserService.JwtToken == null)
products = products.Where(p => p.BeDisplayed);
var roleClaim = _currentUserService.JwtToken?.Claims.FirstOrDefault(c => c.Type == "role");
var roleClaim = currentUserService.JwtToken?.Claims.FirstOrDefault(c => c.Type == "role");
if (roleClaim != null && roleClaim.Value.Contains("Customer"))
products = products.Where(p => p.BeDisplayed);
@ -46,7 +40,7 @@ public class GetProductsQueryHandler : IRequestHandler<GetProductsQuery, GetProd
if (request.CategoryId != default)
{
var cats = await _mediator.Send(new GetProductCategoryChildrenQuery(request.CategoryId), cancellationToken);
var cats = await mediator.Send(new GetProductCategoryChildrenQuery(request.CategoryId), cancellationToken);
products = products.Where(p => cats.Contains(p.CategoryId));
}
if (request.BrandIds is { Length: > 0 })
@ -61,7 +55,7 @@ public class GetProductsQueryHandler : IRequestHandler<GetProductsQuery, GetProd
{
if (request.SpecialOffer.Value)
{
var productDiscount = await _repositoryWrapper.SetRepository<ProductDiscount>()
var productDiscount = await repositoryWrapper.SetRepository<ProductDiscount>()
.TableNoTracking
.Where(d => d.HasCode == false && d.IsSpecialOffer && d.ExpireDate.Date >= DateTime.Today.Date)
.OrderByDescending(d => d.CreatedAt)
@ -92,7 +86,7 @@ public class GetProductsQueryHandler : IRequestHandler<GetProductsQuery, GetProd
foreach (var productSDto in productSDtos)
{
await _mediator.Send(new CalculateProductDiscountCommand(productSDto), cancellationToken);
await mediator.Send(new CalculateProductDiscountCommand(productSDto), cancellationToken);
}
response.Products = productSDtos;

View File

@ -1,8 +1,6 @@
using Microsoft.EntityFrameworkCore;
namespace Netina.Repository.Handlers.Products;
namespace Netina.Repository.Handlers.Products;
public class UpdateProductCommandHandler(IRepositoryWrapper repositoryWrapper, IMediator mediator)
public class UpdateProductCommandHandler(IRepositoryWrapper repositoryWrapper,IMartenRepositoryWrapper martenRepositoryWrapper, IMediator mediator)
: IRequestHandler<UpdateProductCommand, bool>
{
public async Task<bool> Handle(UpdateProductCommand request, CancellationToken cancellationToken)
@ -82,7 +80,34 @@ public class UpdateProductCommandHandler(IRepositoryWrapper repositoryWrapper, I
await mediator.Send(discountRequest, cancellationToken);
}
}
else
{
var discount = await repositoryWrapper.SetRepository<ProductDiscount>()
.TableNoTracking
.FirstOrDefaultAsync(d => d.ProductId == newEnt.Id && d.IsSpecialOffer, cancellationToken);
if (discount != null)
await mediator.Send(new DeleteDiscountCommand(discount.Id), cancellationToken);
}
await UpdateFaqAsync(newEnt, request.Faqs, cancellationToken);
return true;
}
private async Task UpdateFaqAsync(Product newEnt, Dictionary<string, string> faqs, CancellationToken cancellationToken)
{
var url = newEnt.GetWebSiteUrl();
var oldFaq = await martenRepositoryWrapper.SetRepository<BaseFaq>()
.GetEntityAsync(f => f.Slug == newEnt.Slug, cancellationToken);
var newFaq = await martenRepositoryWrapper.SetRepository<BaseFaq>()
.GetEntityAsync(f => f.Slug == url, cancellationToken);
if (oldFaq != null)
await mediator.Send(new DeleteFaqCommand(oldFaq.Id), cancellationToken);
if (newFaq == null)
await mediator.Send(new CreateFaqCommand(faqs, url, newEnt.EnglishName), cancellationToken);
else
await mediator.Send(new UpdateFaqCommand(newFaq.Id, faqs, url, newEnt.EnglishName), cancellationToken);
}
}

View File

@ -45,6 +45,7 @@
<Using Include="MediatR" />
<Using Include="Microsoft.AspNetCore.Builder" />
<Using Include="Microsoft.AspNetCore.Identity" />
<Using Include="Microsoft.EntityFrameworkCore" />
<Using Include="Microsoft.Extensions.DependencyInjection" />
<Using Include="Microsoft.Extensions.Logging" />
<Using Include="Microsoft.Extensions.Options" />
@ -63,7 +64,9 @@
<Using Include="Netina.Domain.Entities.Products" />
<Using Include="Netina.Domain.Entities.Users" />
<Using Include="Netina.Domain.Enums" />
<Using Include="Netina.Domain.Extensions" />
<Using Include="Netina.Domain.Mappers" />
<Using Include="Netina.Domain.MartenEntities.Faqs" />
<Using Include="Netina.Domain.Models.Claims" />
<Using Include="Netina.Domain.Models.Settings" />
<Using Include="Netina.Repository.Abstracts" />
@ -71,6 +74,7 @@
<Using Include="Netina.Repository.Models" />
<Using Include="Netina.Repository.Repositories.Base" />
<Using Include="Netina.Repository.Repositories.Base.Contracts" />
<Using Include="Netina.Repository.Repositories.Marten" />
<Using Include="Netina.Repository.Services.Abstracts" />
<Using Include="Pluralize.NET" />
<Using Include="System.Diagnostics" />

View File

@ -6,10 +6,13 @@ public interface IMartenRepository<TMartenEntity> : IScopedDependency where TMar
{
Task<List<TMartenEntity>> GetEntitiesAsync(CancellationToken cancellation = default);
Task<List<TMartenEntity>> GetEntitiesAsync(Expression<Func<TMartenEntity, bool>> expression, CancellationToken cancellation = default);
Task<List<TMartenEntity>> GetEntitiesAsync(int page, int count, CancellationToken cancellation);
Task<List<TMartenEntity>> GetEntitiesAsync(Expression<Func<TMartenEntity, bool>> expression, int page, int count, CancellationToken cancellation);
Task<TMartenEntity> GetEntityAsync(Guid id, CancellationToken cancellation = default);
Task<TMartenEntity?> GetEntityAsync(Expression<Func<TMartenEntity, bool>> expression, CancellationToken cancellation = default);
Task AddOrUpdateEntityAsync(TMartenEntity entity, CancellationToken cancellation = default);
Task UpdateEntityAsync(TMartenEntity entity, CancellationToken cancellation = default);
Task RemoveEntityAsync(TMartenEntity entity, CancellationToken cancellation = default);
}

View File

@ -1,44 +1,25 @@
using Microsoft.EntityFrameworkCore;
namespace Netina.Repository.Services;
namespace Netina.Repository.Services;
public class DbInitializerService : IDbInitializerService
{
private readonly IOptionsSnapshot<SiteSettings> _adminUserSeedOptions;
private readonly ApplicationContext _context;
private readonly ILogger<DbInitializerService> _logger;
private readonly IRepositoryWrapper _repositoryWrapper;
private readonly RoleManager<ApplicationRole> _roleManager;
private readonly UserManager<ApplicationUser> _userManager;
public DbInitializerService(
public class DbInitializerService(
ApplicationContext context,
RoleManager<ApplicationRole> roleManager,
UserManager<ApplicationUser> userManager,
IOptionsSnapshot<SiteSettings> adminUserSeedOptions,
ILogger<DbInitializerService> logger,
IRepositoryWrapper repositoryWrapper)
{
_context = context;
_roleManager = roleManager;
_userManager = userManager;
_adminUserSeedOptions = adminUserSeedOptions;
_logger = logger;
_repositoryWrapper = repositoryWrapper;
}
: IDbInitializerService
{
public void Initialize()
{
try
{
_context.Database.Migrate();
_context.Database.ExecuteSqlRaw("CREATE EXTENSION IF NOT EXISTS pg_trgm");
_logger.LogInformation("Migration SUCCESS !!!!");
context.Database.Migrate();
context.Database.ExecuteSqlRaw("CREATE EXTENSION IF NOT EXISTS pg_trgm");
logger.LogInformation("Migration SUCCESS !!!!");
}
catch (Exception e)
{
_logger.LogError(e, e.Message);
logger.LogError(e, e.Message);
}
}
@ -47,9 +28,9 @@ public class DbInitializerService : IDbInitializerService
try
{
await SeedRoles();
var seedAdmin = _adminUserSeedOptions.Value.UserSetting;
var manager = _adminUserSeedOptions.Value.Manager;
var user = await _userManager.FindByNameAsync(seedAdmin.Username);
var seedAdmin = adminUserSeedOptions.Value.UserSetting;
var manager = adminUserSeedOptions.Value.Manager;
var user = await userManager.FindByNameAsync(seedAdmin.Username);
if (user == null)
{
var adminUser = new ApplicationUser
@ -65,17 +46,17 @@ public class DbInitializerService : IDbInitializerService
PhoneNumber = seedAdmin.Phone,
BirthDate = DateTime.Now.AddYears(-23)
};
var adminUserResult = await _userManager.CreateAsync(adminUser, seedAdmin.Password);
_repositoryWrapper.SetRepository<Manager>()
var adminUserResult = await userManager.CreateAsync(adminUser, seedAdmin.Password);
repositoryWrapper.SetRepository<Manager>()
.Add(new Manager
{
UserId = adminUser.Id
});
await _repositoryWrapper.SaveChangesAsync(default);
if (adminUserResult.Succeeded) await _userManager.AddToRoleAsync(adminUser, seedAdmin.RoleName);
await repositoryWrapper.SaveChangesAsync(default);
if (adminUserResult.Succeeded) await userManager.AddToRoleAsync(adminUser, seedAdmin.RoleName);
}
var mahanUser = await _userManager.FindByNameAsync(manager.Username);
var mahanUser = await userManager.FindByNameAsync(manager.Username);
if (mahanUser == null)
{
mahanUser = new ApplicationUser
@ -91,14 +72,14 @@ public class DbInitializerService : IDbInitializerService
PhoneNumber = manager.Phone,
BirthDate = DateTime.Now.AddYears(-23)
};
var adminUserResult = await _userManager.CreateAsync(mahanUser, seedAdmin.Password);
_repositoryWrapper.SetRepository<Manager>()
var adminUserResult = await userManager.CreateAsync(mahanUser, seedAdmin.Password);
repositoryWrapper.SetRepository<Manager>()
.Add(new Manager
{
UserId = mahanUser.Id
});
await _repositoryWrapper.SaveChangesAsync(default);
if (adminUserResult.Succeeded) await _userManager.AddToRoleAsync(mahanUser, "Manager");
await repositoryWrapper.SaveChangesAsync(default);
if (adminUserResult.Succeeded) await userManager.AddToRoleAsync(mahanUser, "Manager");
}
}
catch (Exception e)
@ -110,8 +91,8 @@ public class DbInitializerService : IDbInitializerService
public async Task SeedRoles()
{
var seedAdmin = _adminUserSeedOptions.Value.UserSetting;
var rootRole = await _roleManager.FindByNameAsync(seedAdmin.RoleName);
var seedAdmin = adminUserSeedOptions.Value.UserSetting;
var rootRole = await roleManager.FindByNameAsync(seedAdmin.RoleName);
if (rootRole == null)
{
rootRole = new ApplicationRole
@ -120,21 +101,21 @@ public class DbInitializerService : IDbInitializerService
EnglishName = seedAdmin.RoleName,
Description = "root admin role"
};
var adminRoleResult = await _roleManager.CreateAsync(rootRole);
var adminRoleResult = await roleManager.CreateAsync(rootRole);
foreach (var claim in ApplicationClaims.AllClaims)
await _roleManager.AddClaimAsync(rootRole, claim);
await roleManager.AddClaimAsync(rootRole, claim);
}
else
{
foreach (var claim in ApplicationClaims.AllClaims)
{
var claims = await _roleManager.GetClaimsAsync(rootRole);
var claims = await roleManager.GetClaimsAsync(rootRole);
if (claims.FirstOrDefault(c => c.Value == claim.Value) == null)
await _roleManager.AddClaimAsync(rootRole, claim);
await roleManager.AddClaimAsync(rootRole, claim);
}
}
var managerRole = await _roleManager.FindByNameAsync("Manager");
var managerRole = await roleManager.FindByNameAsync("Manager");
if (managerRole == null)
{
managerRole = new ApplicationRole
@ -144,21 +125,21 @@ public class DbInitializerService : IDbInitializerService
PersianName = "مدیریتـــ",
Description = "admin role"
};
var adminRoleResult = await _roleManager.CreateAsync(managerRole);
var adminRoleResult = await roleManager.CreateAsync(managerRole);
foreach (var claim in ApplicationClaims.AllClaims)
await _roleManager.AddClaimAsync(managerRole, claim);
await roleManager.AddClaimAsync(managerRole, claim);
}
else
{
foreach (var claim in ApplicationClaims.AllClaims)
{
var claims = await _roleManager.GetClaimsAsync(managerRole);
var claims = await roleManager.GetClaimsAsync(managerRole);
if (claims.FirstOrDefault(c => c.Value == claim.Value) == null)
await _roleManager.AddClaimAsync(managerRole, claim);
await roleManager.AddClaimAsync(managerRole, claim);
}
}
var customerRole = await _roleManager.FindByNameAsync("Customer");
var customerRole = await roleManager.FindByNameAsync("Customer");
if (customerRole == null)
{
customerRole = new ApplicationRole
@ -168,17 +149,17 @@ public class DbInitializerService : IDbInitializerService
EnglishName = "Customer",
};
var customerRoleResult = await _roleManager.CreateAsync(customerRole);
var customerRoleResult = await roleManager.CreateAsync(customerRole);
foreach (var claim in ApplicationClaims.CustomerClaims)
await _roleManager.AddClaimAsync(customerRole, claim);
await roleManager.AddClaimAsync(customerRole, claim);
}
else
{
foreach (var claim in ApplicationClaims.CustomerClaims)
{
var claims = await _roleManager.GetClaimsAsync(customerRole);
var claims = await roleManager.GetClaimsAsync(customerRole);
if (claims.FirstOrDefault(c => c.Value == claim.Value) == null)
await _roleManager.AddClaimAsync(customerRole, claim);
await roleManager.AddClaimAsync(customerRole, claim);
}
}
}