feat: 增加分页排序与FluentValidation
This commit is contained in:
@@ -112,4 +112,9 @@ public sealed class ProductDto
|
||||
/// 是否推荐。
|
||||
/// </summary>
|
||||
public bool IsFeatured { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间。
|
||||
/// </summary>
|
||||
public DateTime CreatedAt { get; init; }
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ public sealed class CreateProductCommandHandler(IProductRepository productReposi
|
||||
EnableDineIn = product.EnableDineIn,
|
||||
EnablePickup = product.EnablePickup,
|
||||
EnableDelivery = product.EnableDelivery,
|
||||
IsFeatured = product.IsFeatured
|
||||
IsFeatured = product.IsFeatured,
|
||||
CreatedAt = product.CreatedAt
|
||||
};
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ public sealed class GetProductByIdQueryHandler(
|
||||
EnableDineIn = product.EnableDineIn,
|
||||
EnablePickup = product.EnablePickup,
|
||||
EnableDelivery = product.EnableDelivery,
|
||||
IsFeatured = product.IsFeatured
|
||||
IsFeatured = product.IsFeatured,
|
||||
CreatedAt = product.CreatedAt
|
||||
};
|
||||
}
|
||||
|
||||
@@ -28,7 +28,27 @@ public sealed class SearchProductsQueryHandler(
|
||||
products = products.Where(x => x.StoreId == request.StoreId.Value).ToList();
|
||||
}
|
||||
|
||||
return products.Select(MapToDto).ToList();
|
||||
var sorted = ApplySorting(products, request.SortBy, request.SortDescending);
|
||||
var paged = sorted
|
||||
.Skip((request.Page - 1) * request.PageSize)
|
||||
.Take(request.PageSize)
|
||||
.ToList();
|
||||
|
||||
return paged.Select(MapToDto).ToList();
|
||||
}
|
||||
|
||||
private static IOrderedEnumerable<Domain.Products.Entities.Product> ApplySorting(
|
||||
IReadOnlyCollection<Domain.Products.Entities.Product> products,
|
||||
string? sortBy,
|
||||
bool sortDescending)
|
||||
{
|
||||
return sortBy?.ToLowerInvariant() switch
|
||||
{
|
||||
"name" => sortDescending ? products.OrderByDescending(x => x.Name) : products.OrderBy(x => x.Name),
|
||||
"price" => sortDescending ? products.OrderByDescending(x => x.Price) : products.OrderBy(x => x.Price),
|
||||
"status" => sortDescending ? products.OrderByDescending(x => x.Status) : products.OrderBy(x => x.Status),
|
||||
_ => sortDescending ? products.OrderByDescending(x => x.CreatedAt) : products.OrderBy(x => x.CreatedAt)
|
||||
};
|
||||
}
|
||||
|
||||
private static ProductDto MapToDto(Domain.Products.Entities.Product product) => new()
|
||||
@@ -52,6 +72,7 @@ public sealed class SearchProductsQueryHandler(
|
||||
EnableDineIn = product.EnableDineIn,
|
||||
EnablePickup = product.EnablePickup,
|
||||
EnableDelivery = product.EnableDelivery,
|
||||
IsFeatured = product.IsFeatured
|
||||
IsFeatured = product.IsFeatured,
|
||||
CreatedAt = product.CreatedAt
|
||||
};
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ public sealed class UpdateProductCommandHandler(
|
||||
EnableDineIn = product.EnableDineIn,
|
||||
EnablePickup = product.EnablePickup,
|
||||
EnableDelivery = product.EnableDelivery,
|
||||
IsFeatured = product.IsFeatured
|
||||
IsFeatured = product.IsFeatured,
|
||||
CreatedAt = product.CreatedAt
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,4 +23,24 @@ public sealed class SearchProductsQuery : IRequest<IReadOnlyList<ProductDto>>
|
||||
/// 状态过滤。
|
||||
/// </summary>
|
||||
public ProductStatus? Status { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 页码。
|
||||
/// </summary>
|
||||
public int Page { get; init; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// 每页条数。
|
||||
/// </summary>
|
||||
public int PageSize { get; init; } = 20;
|
||||
|
||||
/// <summary>
|
||||
/// 排序字段(name/price/status/createdAt)。
|
||||
/// </summary>
|
||||
public string? SortBy { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否倒序。
|
||||
/// </summary>
|
||||
public bool SortDescending { get; init; } = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
using FluentValidation;
|
||||
using TakeoutSaaS.Application.App.Products.Commands;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Products.Validators;
|
||||
|
||||
/// <summary>
|
||||
/// 创建商品命令验证器。
|
||||
/// </summary>
|
||||
public sealed class CreateProductCommandValidator : AbstractValidator<CreateProductCommand>
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化验证规则。
|
||||
/// </summary>
|
||||
public CreateProductCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.StoreId).GreaterThan(0);
|
||||
RuleFor(x => x.CategoryId).GreaterThan(0);
|
||||
RuleFor(x => x.SpuCode).NotEmpty().MaximumLength(32);
|
||||
RuleFor(x => x.Name).NotEmpty().MaximumLength(128);
|
||||
RuleFor(x => x.Subtitle).MaximumLength(256);
|
||||
RuleFor(x => x.Unit).MaximumLength(16);
|
||||
RuleFor(x => x.Price).GreaterThanOrEqualTo(0);
|
||||
RuleFor(x => x.OriginalPrice).GreaterThanOrEqualTo(0).When(x => x.OriginalPrice.HasValue);
|
||||
RuleFor(x => x.StockQuantity).GreaterThanOrEqualTo(0).When(x => x.StockQuantity.HasValue);
|
||||
RuleFor(x => x.MaxQuantityPerOrder).GreaterThan(0).When(x => x.MaxQuantityPerOrder.HasValue);
|
||||
RuleFor(x => x.CoverImage).MaximumLength(256);
|
||||
RuleFor(x => x.GalleryImages).MaximumLength(1024);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using FluentValidation;
|
||||
using TakeoutSaaS.Application.App.Products.Queries;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Products.Validators;
|
||||
|
||||
/// <summary>
|
||||
/// 商品列表查询验证器。
|
||||
/// </summary>
|
||||
public sealed class SearchProductsQueryValidator : AbstractValidator<SearchProductsQuery>
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化验证规则。
|
||||
/// </summary>
|
||||
public SearchProductsQueryValidator()
|
||||
{
|
||||
RuleFor(x => x.Page).GreaterThan(0);
|
||||
RuleFor(x => x.PageSize).InclusiveBetween(1, 200);
|
||||
RuleFor(x => x.SortBy).MaximumLength(64);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using FluentValidation;
|
||||
using TakeoutSaaS.Application.App.Products.Commands;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Products.Validators;
|
||||
|
||||
/// <summary>
|
||||
/// 更新商品命令验证器。
|
||||
/// </summary>
|
||||
public sealed class UpdateProductCommandValidator : AbstractValidator<UpdateProductCommand>
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化验证规则。
|
||||
/// </summary>
|
||||
public UpdateProductCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.ProductId).GreaterThan(0);
|
||||
RuleFor(x => x.StoreId).GreaterThan(0);
|
||||
RuleFor(x => x.CategoryId).GreaterThan(0);
|
||||
RuleFor(x => x.SpuCode).NotEmpty().MaximumLength(32);
|
||||
RuleFor(x => x.Name).NotEmpty().MaximumLength(128);
|
||||
RuleFor(x => x.Subtitle).MaximumLength(256);
|
||||
RuleFor(x => x.Unit).MaximumLength(16);
|
||||
RuleFor(x => x.Price).GreaterThanOrEqualTo(0);
|
||||
RuleFor(x => x.OriginalPrice).GreaterThanOrEqualTo(0).When(x => x.OriginalPrice.HasValue);
|
||||
RuleFor(x => x.StockQuantity).GreaterThanOrEqualTo(0).When(x => x.StockQuantity.HasValue);
|
||||
RuleFor(x => x.MaxQuantityPerOrder).GreaterThan(0).When(x => x.MaxQuantityPerOrder.HasValue);
|
||||
RuleFor(x => x.CoverImage).MaximumLength(256);
|
||||
RuleFor(x => x.GalleryImages).MaximumLength(1024);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user