Add Get queries for variants and categories

This commit is contained in:
lumijiez
2025-05-27 13:26:38 +03:00
parent 6e59f46cf0
commit a30ada5061
8 changed files with 276 additions and 12 deletions

View File

@@ -0,0 +1,40 @@
using MediatR;
using Printbase.Application.Products.Dtos;
using Printbase.Application.Products.Queries;
namespace Printbase.Application.Products.Handlers;
public class GetCategoriesHandler(IUnitOfWork unitOfWork)
: IRequestHandler<GetCategoriesQuery, IEnumerable<CategoryDto>>
{
public async Task<IEnumerable<CategoryDto>> Handle(GetCategoriesQuery request, CancellationToken cancellationToken)
{
IEnumerable<Domain.Entities.Product.Category> categories;
if (request.RootCategoriesOnly)
{
categories = await unitOfWork.CategoryRepository.GetRootCategoriesAsync(cancellationToken);
}
else if (request.IsActive.HasValue && request.IsActive.Value)
{
categories = await unitOfWork.CategoryRepository.GetActiveAsync(cancellationToken);
}
else
{
categories = await unitOfWork.CategoryRepository.GetAllAsync(cancellationToken);
}
return categories.Select(c => new CategoryDto
{
Id = c.Id,
Name = c.Name,
Description = c.Description,
ImageUrl = c.ImageUrl,
SortOrder = c.SortOrder,
IsActive = c.IsActive,
ParentCategoryId = c.ParentCategoryId,
CreatedAt = c.CreatedAt,
ModifiedAt = c.ModifiedAt
});
}
}

View File

@@ -0,0 +1,62 @@
using MediatR;
using Printbase.Application.Products.Dtos;
using Printbase.Application.Products.Queries;
namespace Printbase.Application.Products.Handlers;
public class GetProductVariantsHandler(IUnitOfWork unitOfWork)
: IRequestHandler<GetProductVariantsQuery, IEnumerable<ProductVariantDto>>
{
public async Task<IEnumerable<ProductVariantDto>> Handle(GetProductVariantsQuery request, CancellationToken cancellationToken)
{
IEnumerable<Domain.Entities.Product.ProductVariant> variants;
if (request.ProductId.HasValue)
{
if (request.InStockOnly)
{
variants = await unitOfWork.ProductVariantRepository.GetInStockByProductIdAsync(request.ProductId.Value, cancellationToken);
}
else if (request.IsActive.HasValue && request.IsActive.Value)
{
variants = await unitOfWork.ProductVariantRepository.GetActiveByProductIdAsync(request.ProductId.Value, cancellationToken);
}
else
{
variants = await unitOfWork.ProductVariantRepository.GetByProductIdAsync(request.ProductId.Value, cancellationToken);
}
}
else
{
variants = new List<Domain.Entities.Product.ProductVariant>();
}
return variants.Select(pv => new ProductVariantDto
{
Id = pv.Id,
ProductId = pv.ProductId,
Size = pv.Size,
Color = pv.Color,
Price = pv.Price,
ImageUrl = pv.ImageUrl,
Sku = pv.Sku,
StockQuantity = pv.StockQuantity,
IsActive = pv.IsActive,
Product = new ProductDto
{
Id = pv.Product.Id,
Name = pv.Product.Name,
Description = pv.Product.Description,
BasePrice = pv.Product.BasePrice,
IsCustomizable = pv.Product.IsCustomizable,
IsActive = pv.Product.IsActive,
ImageUrl = pv.Product.ImageUrl,
CategoryId = pv.Product.CategoryId,
CreatedAt = pv.Product.CreatedAt,
ModifiedAt = pv.Product.ModifiedAt
},
CreatedAt = pv.CreatedAt,
ModifiedAt = pv.ModifiedAt
});
}
}

View File

@@ -0,0 +1,10 @@
using MediatR;
using Printbase.Application.Products.Dtos;
namespace Printbase.Application.Products.Queries;
public class GetCategoriesQuery : IRequest<IEnumerable<CategoryDto>>
{
public bool? IsActive { get; set; }
public bool RootCategoriesOnly { get; set; } = false;
}

View File

@@ -0,0 +1,11 @@
using MediatR;
using Printbase.Application.Products.Dtos;
namespace Printbase.Application.Products.Queries;
public class GetProductVariantsQuery : IRequest<IEnumerable<ProductVariantDto>>
{
public Guid? ProductId { get; set; }
public bool? IsActive { get; set; }
public bool InStockOnly { get; set; } = false;
}

View File

@@ -0,0 +1,30 @@
using MediatR;
using Microsoft.AspNetCore.Mvc;
using Printbase.Application.Products.Commands;
using Printbase.Application.Products.Dtos;
namespace Printbase.WebApi.Controllers;
[ApiController]
[Route("api/[controller]")]
public class CategoriesController(IMediator mediator) : ControllerBase
{
[HttpPost]
public async Task<ActionResult<CategoryDto>> CreateCategory([FromBody] CreateCategoryCommand command)
{
var result = await mediator.Send(command);
return CreatedAtAction(nameof(CreateCategory), new { id = result.Id }, result);
}
[HttpDelete("{id:guid}")]
public async Task<ActionResult> DeleteCategory(Guid id)
{
var command = new DeleteCategoryCommand { Id = id };
var result = await mediator.Send(command);
if (!result)
return NotFound();
return NoContent();
}
}

View File

@@ -0,0 +1,30 @@
using MediatR;
using Microsoft.AspNetCore.Mvc;
using Printbase.Application.Products.Commands;
using Printbase.Application.Products.Dtos;
namespace Printbase.WebApi.Controllers;
[ApiController]
[Route("api/[controller]")]
public class ProductVariantsController(IMediator mediator) : ControllerBase
{
[HttpPost]
public async Task<ActionResult<ProductVariantDto>> CreateProductVariant([FromBody] CreateProductVariantCommand command)
{
var result = await mediator.Send(command);
return CreatedAtAction(nameof(CreateProductVariant), new { id = result.Id }, result);
}
[HttpDelete("{id}")]
public async Task<ActionResult> DeleteProductVariant(Guid id)
{
var command = new DeleteProductVariantCommand { Id = id };
var result = await mediator.Send(command);
if (!result)
return NotFound();
return NoContent();
}
}

View File

@@ -0,0 +1,65 @@
using MediatR;
using Microsoft.AspNetCore.Mvc;
using Printbase.Application.Products.Commands;
using Printbase.Application.Products.Dtos;
using Printbase.Application.Products.Queries;
using Printbase.Domain.Common.Models;
namespace Printbase.WebApi.Controllers;
[ApiController]
[Route("api/[controller]")]
public class ProductsController(IMediator mediator) : ControllerBase
{
[HttpGet]
public async Task<ActionResult<PagedResultDto<ProductDto>>> GetProducts(
[FromQuery] int pageNumber = 1,
[FromQuery] int pageSize = 10,
[FromQuery] string? searchTerm = null,
[FromQuery] Guid? categoryId = null,
[FromQuery] decimal? minPrice = null,
[FromQuery] decimal? maxPrice = null,
[FromQuery] bool? isActive = true,
[FromQuery] bool? isCustomizable = null,
[FromQuery] string sortBy = "Name",
[FromQuery] string sortDirection = "ASC")
{
var filterParameters = new ProductFilterParameters
{
PageNumber = pageNumber,
PageSize = pageSize,
SearchTerm = searchTerm,
CategoryId = categoryId,
MinPrice = minPrice,
MaxPrice = maxPrice,
IsActive = isActive,
IsCustomizable = isCustomizable,
SortBy = sortBy,
SortDirection = sortDirection
};
var query = new GetProductsQuery { FilterParameters = filterParameters };
var result = await mediator.Send(query);
return Ok(result);
}
[HttpPost]
public async Task<ActionResult<ProductDto>> CreateProduct([FromBody] CreateProductCommand command)
{
var result = await mediator.Send(command);
return CreatedAtAction(nameof(CreateProduct), new { id = result.Id }, result);
}
[HttpDelete("{id:guid}")]
public async Task<ActionResult> DeleteProduct(Guid id)
{
var command = new DeleteProductCommand { Id = id };
var result = await mediator.Send(command);
if (!result)
return NotFound();
return NoContent();
}
}

View File

@@ -1,8 +1,12 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Printbase.Application;
using Printbase.Application.Products.Handlers;
using Printbase.Domain.Entities.Users;
using Printbase.Domain.Repositories;
using Printbase.Infrastructure;
using Printbase.Infrastructure.Database;
using Printbase.Infrastructure.Repositories;
namespace Printbase.WebApi;
@@ -12,6 +16,11 @@ public static class Startup
{
var services = builder.Services;
services.AddScoped<IProductRepository, ProductRepository>();
services.AddScoped<IProductVariantRepository, ProductVariantRepository>();
services.AddScoped<ICategoryRepository, CategoryRepository>();
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
builder.Configuration.GetConnectionString("DefaultConnection"),
@@ -67,6 +76,9 @@ public static class Startup
policy.RequireRole("Administrator", "ProductManager"))
.AddPolicy("CustomerPolicy", policy =>
policy.RequireRole("Customer", "Administrator", "OrderManager", "ProductManager"));
services.AddControllers();
services.AddSwaggerGen();
}
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
@@ -86,18 +98,22 @@ public static class Startup
}
}
if (env.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
app.UseHttpsRedirection();
}
// if (env.IsDevelopment())
// {
// app.UseSwagger();
// app.UseSwaggerUI();
// app.UseDeveloperExceptionPage();
// }
// else
// {
// app.UseExceptionHandler("/Error");
// app.UseHsts();
// app.UseHttpsRedirection();
// }
app.UseSwagger();
app.UseSwaggerUI();
app.UseDeveloperExceptionPage();
app.UseRouting();