feat: 增加分页排序与FluentValidation
This commit is contained in:
@@ -111,4 +111,9 @@ public sealed class StoreDto
|
||||
/// 支持配送。
|
||||
/// </summary>
|
||||
public bool SupportsDelivery { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间。
|
||||
/// </summary>
|
||||
public DateTime CreatedAt { get; init; }
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ public sealed class CreateStoreCommandHandler(IStoreRepository storeRepository,
|
||||
DeliveryRadiusKm = store.DeliveryRadiusKm,
|
||||
SupportsDineIn = store.SupportsDineIn,
|
||||
SupportsPickup = store.SupportsPickup,
|
||||
SupportsDelivery = store.SupportsDelivery
|
||||
SupportsDelivery = store.SupportsDelivery,
|
||||
CreatedAt = store.CreatedAt
|
||||
};
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ public sealed class GetStoreByIdQueryHandler(
|
||||
DeliveryRadiusKm = store.DeliveryRadiusKm,
|
||||
SupportsDineIn = store.SupportsDineIn,
|
||||
SupportsPickup = store.SupportsPickup,
|
||||
SupportsDelivery = store.SupportsDelivery
|
||||
SupportsDelivery = store.SupportsDelivery,
|
||||
CreatedAt = store.CreatedAt
|
||||
};
|
||||
}
|
||||
|
||||
@@ -28,9 +28,27 @@ public sealed class SearchStoresQueryHandler(
|
||||
stores = stores.Where(x => x.MerchantId == request.MerchantId.Value).ToList();
|
||||
}
|
||||
|
||||
return stores
|
||||
.Select(MapToDto)
|
||||
var sorted = ApplySorting(stores, 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.Stores.Entities.Store> ApplySorting(
|
||||
IReadOnlyCollection<Domain.Stores.Entities.Store> stores,
|
||||
string? sortBy,
|
||||
bool sortDescending)
|
||||
{
|
||||
return sortBy?.ToLowerInvariant() switch
|
||||
{
|
||||
"name" => sortDescending ? stores.OrderByDescending(x => x.Name) : stores.OrderBy(x => x.Name),
|
||||
"code" => sortDescending ? stores.OrderByDescending(x => x.Code) : stores.OrderBy(x => x.Code),
|
||||
"status" => sortDescending ? stores.OrderByDescending(x => x.Status) : stores.OrderBy(x => x.Status),
|
||||
_ => sortDescending ? stores.OrderByDescending(x => x.CreatedAt) : stores.OrderBy(x => x.CreatedAt)
|
||||
};
|
||||
}
|
||||
|
||||
private static StoreDto MapToDto(Domain.Stores.Entities.Store store) => new()
|
||||
@@ -54,6 +72,7 @@ public sealed class SearchStoresQueryHandler(
|
||||
DeliveryRadiusKm = store.DeliveryRadiusKm,
|
||||
SupportsDineIn = store.SupportsDineIn,
|
||||
SupportsPickup = store.SupportsPickup,
|
||||
SupportsDelivery = store.SupportsDelivery
|
||||
SupportsDelivery = store.SupportsDelivery,
|
||||
CreatedAt = store.CreatedAt
|
||||
};
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ public sealed class UpdateStoreCommandHandler(
|
||||
DeliveryRadiusKm = store.DeliveryRadiusKm,
|
||||
SupportsDineIn = store.SupportsDineIn,
|
||||
SupportsPickup = store.SupportsPickup,
|
||||
SupportsDelivery = store.SupportsDelivery
|
||||
SupportsDelivery = store.SupportsDelivery,
|
||||
CreatedAt = store.CreatedAt
|
||||
};
|
||||
}
|
||||
|
||||
@@ -18,4 +18,24 @@ public sealed class SearchStoresQuery : IRequest<IReadOnlyList<StoreDto>>
|
||||
/// 状态过滤。
|
||||
/// </summary>
|
||||
public StoreStatus? Status { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 页码。
|
||||
/// </summary>
|
||||
public int Page { get; init; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// 每页条数。
|
||||
/// </summary>
|
||||
public int PageSize { get; init; } = 20;
|
||||
|
||||
/// <summary>
|
||||
/// 排序字段(name/code/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.Stores.Commands;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Validators;
|
||||
|
||||
/// <summary>
|
||||
/// 创建门店命令验证器。
|
||||
/// </summary>
|
||||
public sealed class CreateStoreCommandValidator : AbstractValidator<CreateStoreCommand>
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化验证规则。
|
||||
/// </summary>
|
||||
public CreateStoreCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.MerchantId).GreaterThan(0);
|
||||
RuleFor(x => x.Code).NotEmpty().MaximumLength(32);
|
||||
RuleFor(x => x.Name).NotEmpty().MaximumLength(128);
|
||||
RuleFor(x => x.Phone).MaximumLength(32);
|
||||
RuleFor(x => x.ManagerName).MaximumLength(64);
|
||||
RuleFor(x => x.Province).MaximumLength(64);
|
||||
RuleFor(x => x.City).MaximumLength(64);
|
||||
RuleFor(x => x.District).MaximumLength(64);
|
||||
RuleFor(x => x.Address).MaximumLength(256);
|
||||
RuleFor(x => x.Announcement).MaximumLength(512);
|
||||
RuleFor(x => x.Tags).MaximumLength(256);
|
||||
RuleFor(x => x.DeliveryRadiusKm).GreaterThanOrEqualTo(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using FluentValidation;
|
||||
using TakeoutSaaS.Application.App.Stores.Queries;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Validators;
|
||||
|
||||
/// <summary>
|
||||
/// 门店列表查询验证器。
|
||||
/// </summary>
|
||||
public sealed class SearchStoresQueryValidator : AbstractValidator<SearchStoresQuery>
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化验证规则。
|
||||
/// </summary>
|
||||
public SearchStoresQueryValidator()
|
||||
{
|
||||
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.Stores.Commands;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Validators;
|
||||
|
||||
/// <summary>
|
||||
/// 更新门店命令验证器。
|
||||
/// </summary>
|
||||
public sealed class UpdateStoreCommandValidator : AbstractValidator<UpdateStoreCommand>
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化验证规则。
|
||||
/// </summary>
|
||||
public UpdateStoreCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.StoreId).GreaterThan(0);
|
||||
RuleFor(x => x.MerchantId).GreaterThan(0);
|
||||
RuleFor(x => x.Code).NotEmpty().MaximumLength(32);
|
||||
RuleFor(x => x.Name).NotEmpty().MaximumLength(128);
|
||||
RuleFor(x => x.Phone).MaximumLength(32);
|
||||
RuleFor(x => x.ManagerName).MaximumLength(64);
|
||||
RuleFor(x => x.Province).MaximumLength(64);
|
||||
RuleFor(x => x.City).MaximumLength(64);
|
||||
RuleFor(x => x.District).MaximumLength(64);
|
||||
RuleFor(x => x.Address).MaximumLength(256);
|
||||
RuleFor(x => x.Announcement).MaximumLength(512);
|
||||
RuleFor(x => x.Tags).MaximumLength(256);
|
||||
RuleFor(x => x.DeliveryRadiusKm).GreaterThanOrEqualTo(0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user