feat: 增加分页排序与FluentValidation
This commit is contained in:
@@ -60,4 +60,9 @@ public sealed class MerchantDto
|
||||
/// 入驻时间。
|
||||
/// </summary>
|
||||
public DateTime? JoinedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间。
|
||||
/// </summary>
|
||||
public DateTime CreatedAt { get; init; }
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ public sealed class CreateMerchantCommandHandler(IMerchantRepository merchantRep
|
||||
ContactPhone = merchant.ContactPhone,
|
||||
ContactEmail = merchant.ContactEmail,
|
||||
Status = merchant.Status,
|
||||
JoinedAt = merchant.JoinedAt
|
||||
JoinedAt = merchant.JoinedAt,
|
||||
CreatedAt = merchant.CreatedAt
|
||||
};
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ public sealed class GetMerchantByIdQueryHandler(IMerchantRepository merchantRepo
|
||||
ContactPhone = merchant.ContactPhone,
|
||||
ContactEmail = merchant.ContactEmail,
|
||||
Status = merchant.Status,
|
||||
JoinedAt = merchant.JoinedAt
|
||||
JoinedAt = merchant.JoinedAt,
|
||||
CreatedAt = merchant.CreatedAt
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,20 +23,44 @@ public sealed class SearchMerchantsQueryHandler(
|
||||
var tenantId = _tenantProvider.GetCurrentTenantId();
|
||||
var merchants = await _merchantRepository.SearchAsync(tenantId, request.Status, cancellationToken);
|
||||
|
||||
return merchants
|
||||
.Select(merchant => new MerchantDto
|
||||
{
|
||||
Id = merchant.Id,
|
||||
TenantId = merchant.TenantId,
|
||||
BrandName = merchant.BrandName,
|
||||
BrandAlias = merchant.BrandAlias,
|
||||
LogoUrl = merchant.LogoUrl,
|
||||
Category = merchant.Category,
|
||||
ContactPhone = merchant.ContactPhone,
|
||||
ContactEmail = merchant.ContactEmail,
|
||||
Status = merchant.Status,
|
||||
JoinedAt = merchant.JoinedAt
|
||||
})
|
||||
var sorted = ApplySorting(merchants, request.SortBy, request.SortDescending);
|
||||
var paged = sorted
|
||||
.Skip((request.Page - 1) * request.PageSize)
|
||||
.Take(request.PageSize)
|
||||
.ToList();
|
||||
|
||||
return paged.Select(merchant => new MerchantDto
|
||||
{
|
||||
Id = merchant.Id,
|
||||
TenantId = merchant.TenantId,
|
||||
BrandName = merchant.BrandName,
|
||||
BrandAlias = merchant.BrandAlias,
|
||||
LogoUrl = merchant.LogoUrl,
|
||||
Category = merchant.Category,
|
||||
ContactPhone = merchant.ContactPhone,
|
||||
ContactEmail = merchant.ContactEmail,
|
||||
Status = merchant.Status,
|
||||
JoinedAt = merchant.JoinedAt,
|
||||
CreatedAt = merchant.CreatedAt
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
private static IOrderedEnumerable<Domain.Merchants.Entities.Merchant> ApplySorting(
|
||||
IReadOnlyCollection<Domain.Merchants.Entities.Merchant> merchants,
|
||||
string? sortBy,
|
||||
bool sortDescending)
|
||||
{
|
||||
return sortBy?.ToLowerInvariant() switch
|
||||
{
|
||||
"brandname" => sortDescending
|
||||
? merchants.OrderByDescending(x => x.BrandName)
|
||||
: merchants.OrderBy(x => x.BrandName),
|
||||
"status" => sortDescending
|
||||
? merchants.OrderByDescending(x => x.Status)
|
||||
: merchants.OrderBy(x => x.Status),
|
||||
_ => sortDescending
|
||||
? merchants.OrderByDescending(x => x.CreatedAt)
|
||||
: merchants.OrderBy(x => x.CreatedAt)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ public sealed class UpdateMerchantCommandHandler(
|
||||
ContactPhone = merchant.ContactPhone,
|
||||
ContactEmail = merchant.ContactEmail,
|
||||
Status = merchant.Status,
|
||||
JoinedAt = merchant.JoinedAt
|
||||
JoinedAt = merchant.JoinedAt,
|
||||
CreatedAt = merchant.CreatedAt
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,4 +13,24 @@ public sealed class SearchMerchantsQuery : IRequest<IReadOnlyList<MerchantDto>>
|
||||
/// 按状态过滤。
|
||||
/// </summary>
|
||||
public MerchantStatus? Status { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 页码(从 1 开始)。
|
||||
/// </summary>
|
||||
public int Page { get; init; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// 每页条数。
|
||||
/// </summary>
|
||||
public int PageSize { get; init; } = 20;
|
||||
|
||||
/// <summary>
|
||||
/// 排序字段(brandName/status/createdAt)。
|
||||
/// </summary>
|
||||
public string? SortBy { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否倒序。
|
||||
/// </summary>
|
||||
public bool SortDescending { get; init; } = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
using FluentValidation;
|
||||
using TakeoutSaaS.Application.App.Merchants.Commands;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Merchants.Validators;
|
||||
|
||||
/// <summary>
|
||||
/// 创建商户命令验证器。
|
||||
/// </summary>
|
||||
public sealed class CreateMerchantCommandValidator : AbstractValidator<CreateMerchantCommand>
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化验证规则。
|
||||
/// </summary>
|
||||
public CreateMerchantCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.BrandName).NotEmpty().MaximumLength(128);
|
||||
RuleFor(x => x.BrandAlias).MaximumLength(64);
|
||||
RuleFor(x => x.LogoUrl).MaximumLength(256);
|
||||
RuleFor(x => x.Category).MaximumLength(64);
|
||||
RuleFor(x => x.ContactPhone).NotEmpty().MaximumLength(32);
|
||||
RuleFor(x => x.ContactEmail).EmailAddress().When(x => !string.IsNullOrWhiteSpace(x.ContactEmail));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using FluentValidation;
|
||||
using TakeoutSaaS.Application.App.Merchants.Queries;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Merchants.Validators;
|
||||
|
||||
/// <summary>
|
||||
/// 商户列表查询验证器。
|
||||
/// </summary>
|
||||
public sealed class SearchMerchantsQueryValidator : AbstractValidator<SearchMerchantsQuery>
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化验证规则。
|
||||
/// </summary>
|
||||
public SearchMerchantsQueryValidator()
|
||||
{
|
||||
RuleFor(x => x.Page).GreaterThan(0);
|
||||
RuleFor(x => x.PageSize).InclusiveBetween(1, 200);
|
||||
RuleFor(x => x.SortBy).MaximumLength(64);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using FluentValidation;
|
||||
using TakeoutSaaS.Application.App.Merchants.Commands;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Merchants.Validators;
|
||||
|
||||
/// <summary>
|
||||
/// 更新商户命令验证器。
|
||||
/// </summary>
|
||||
public sealed class UpdateMerchantCommandValidator : AbstractValidator<UpdateMerchantCommand>
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化验证规则。
|
||||
/// </summary>
|
||||
public UpdateMerchantCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.MerchantId).GreaterThan(0);
|
||||
RuleFor(x => x.BrandName).NotEmpty().MaximumLength(128);
|
||||
RuleFor(x => x.BrandAlias).MaximumLength(64);
|
||||
RuleFor(x => x.LogoUrl).MaximumLength(256);
|
||||
RuleFor(x => x.Category).MaximumLength(64);
|
||||
RuleFor(x => x.ContactPhone).NotEmpty().MaximumLength(32);
|
||||
RuleFor(x => x.ContactEmail).EmailAddress().When(x => !string.IsNullOrWhiteSpace(x.ContactEmail));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user