feat: 增加分页排序与FluentValidation

This commit is contained in:
2025-12-02 10:50:43 +08:00
parent 93141fbf0c
commit 97bf6cacb0
63 changed files with 904 additions and 49 deletions

View File

@@ -81,4 +81,9 @@ public sealed class DeliveryOrderDto
/// 事件列表。
/// </summary>
public IReadOnlyList<DeliveryEventDto> Events { get; init; } = Array.Empty<DeliveryEventDto>();
/// <summary>
/// 创建时间。
/// </summary>
public DateTime CreatedAt { get; init; }
}

View File

@@ -56,6 +56,7 @@ public sealed class CreateDeliveryOrderCommandHandler(IDeliveryRepository delive
PickedUpAt = deliveryOrder.PickedUpAt,
DeliveredAt = deliveryOrder.DeliveredAt,
FailureReason = deliveryOrder.FailureReason,
CreatedAt = deliveryOrder.CreatedAt,
Events = events.Select(x => new DeliveryEventDto
{
Id = x.Id,

View File

@@ -47,6 +47,7 @@ public sealed class GetDeliveryOrderByIdQueryHandler(
PickedUpAt = deliveryOrder.PickedUpAt,
DeliveredAt = deliveryOrder.DeliveredAt,
FailureReason = deliveryOrder.FailureReason,
CreatedAt = deliveryOrder.CreatedAt,
Events = events.Select(x => new DeliveryEventDto
{
Id = x.Id,

View File

@@ -23,7 +23,13 @@ public sealed class SearchDeliveryOrdersQueryHandler(
var tenantId = _tenantProvider.GetCurrentTenantId();
var orders = await _deliveryRepository.SearchAsync(tenantId, request.Status, request.OrderId, cancellationToken);
return orders.Select(order => new DeliveryOrderDto
var sorted = ApplySorting(orders, request.SortBy, request.SortDescending);
var paged = sorted
.Skip((request.Page - 1) * request.PageSize)
.Take(request.PageSize)
.ToList();
return paged.Select(order => new DeliveryOrderDto
{
Id = order.Id,
TenantId = order.TenantId,
@@ -37,7 +43,21 @@ public sealed class SearchDeliveryOrdersQueryHandler(
DispatchedAt = order.DispatchedAt,
PickedUpAt = order.PickedUpAt,
DeliveredAt = order.DeliveredAt,
FailureReason = order.FailureReason
FailureReason = order.FailureReason,
CreatedAt = order.CreatedAt
}).ToList();
}
private static IOrderedEnumerable<Domain.Deliveries.Entities.DeliveryOrder> ApplySorting(
IReadOnlyCollection<Domain.Deliveries.Entities.DeliveryOrder> orders,
string? sortBy,
bool sortDescending)
{
return sortBy?.ToLowerInvariant() switch
{
"status" => sortDescending ? orders.OrderByDescending(x => x.Status) : orders.OrderBy(x => x.Status),
"provider" => sortDescending ? orders.OrderByDescending(x => x.Provider) : orders.OrderBy(x => x.Provider),
_ => sortDescending ? orders.OrderByDescending(x => x.CreatedAt) : orders.OrderBy(x => x.CreatedAt)
};
}
}

View File

@@ -66,6 +66,7 @@ public sealed class UpdateDeliveryOrderCommandHandler(
PickedUpAt = deliveryOrder.PickedUpAt,
DeliveredAt = deliveryOrder.DeliveredAt,
FailureReason = deliveryOrder.FailureReason,
CreatedAt = deliveryOrder.CreatedAt,
Events = events.Select(x => new DeliveryEventDto
{
Id = x.Id,

View File

@@ -18,4 +18,24 @@ public sealed class SearchDeliveryOrdersQuery : IRequest<IReadOnlyList<DeliveryO
/// 配送状态。
/// </summary>
public DeliveryStatus? Status { get; init; }
/// <summary>
/// 页码。
/// </summary>
public int Page { get; init; } = 1;
/// <summary>
/// 每页条数。
/// </summary>
public int PageSize { get; init; } = 20;
/// <summary>
/// 排序字段createdAt/status/provider
/// </summary>
public string? SortBy { get; init; }
/// <summary>
/// 是否倒序。
/// </summary>
public bool SortDescending { get; init; } = true;
}

View File

@@ -0,0 +1,23 @@
using FluentValidation;
using TakeoutSaaS.Application.App.Deliveries.Commands;
namespace TakeoutSaaS.Application.App.Deliveries.Validators;
/// <summary>
/// 创建配送单命令验证器。
/// </summary>
public sealed class CreateDeliveryOrderCommandValidator : AbstractValidator<CreateDeliveryOrderCommand>
{
/// <summary>
/// 初始化验证规则。
/// </summary>
public CreateDeliveryOrderCommandValidator()
{
RuleFor(x => x.OrderId).GreaterThan(0);
RuleFor(x => x.ProviderOrderId).MaximumLength(64);
RuleFor(x => x.CourierName).MaximumLength(64);
RuleFor(x => x.CourierPhone).MaximumLength(32);
RuleFor(x => x.FailureReason).MaximumLength(256);
RuleFor(x => x.DeliveryFee).GreaterThanOrEqualTo(0).When(x => x.DeliveryFee.HasValue);
}
}

View File

@@ -0,0 +1,20 @@
using FluentValidation;
using TakeoutSaaS.Application.App.Deliveries.Queries;
namespace TakeoutSaaS.Application.App.Deliveries.Validators;
/// <summary>
/// 配送单列表查询验证器。
/// </summary>
public sealed class SearchDeliveryOrdersQueryValidator : AbstractValidator<SearchDeliveryOrdersQuery>
{
/// <summary>
/// 初始化验证规则。
/// </summary>
public SearchDeliveryOrdersQueryValidator()
{
RuleFor(x => x.Page).GreaterThan(0);
RuleFor(x => x.PageSize).InclusiveBetween(1, 200);
RuleFor(x => x.SortBy).MaximumLength(64);
}
}

View File

@@ -0,0 +1,24 @@
using FluentValidation;
using TakeoutSaaS.Application.App.Deliveries.Commands;
namespace TakeoutSaaS.Application.App.Deliveries.Validators;
/// <summary>
/// 更新配送单命令验证器。
/// </summary>
public sealed class UpdateDeliveryOrderCommandValidator : AbstractValidator<UpdateDeliveryOrderCommand>
{
/// <summary>
/// 初始化验证规则。
/// </summary>
public UpdateDeliveryOrderCommandValidator()
{
RuleFor(x => x.DeliveryOrderId).GreaterThan(0);
RuleFor(x => x.OrderId).GreaterThan(0);
RuleFor(x => x.ProviderOrderId).MaximumLength(64);
RuleFor(x => x.CourierName).MaximumLength(64);
RuleFor(x => x.CourierPhone).MaximumLength(32);
RuleFor(x => x.FailureReason).MaximumLength(256);
RuleFor(x => x.DeliveryFee).GreaterThanOrEqualTo(0).When(x => x.DeliveryFee.HasValue);
}
}