feat: tenants 列表支持名称/联系人/电话/认证状态过滤
This commit is contained in:
@@ -15,26 +15,47 @@ public sealed class SearchTenantsQueryHandler(ITenantRepository tenantRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<PagedResult<TenantDto>> Handle(SearchTenantsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 查询租户列表
|
||||
var tenants = await tenantRepository.SearchAsync(request.Status, request.Keyword, cancellationToken);
|
||||
var total = tenants.Count;
|
||||
// 1. 按条件分页查询租户
|
||||
var (tenants, total) = await tenantRepository.SearchPagedAsync(
|
||||
request.Status,
|
||||
request.VerificationStatus,
|
||||
request.Name,
|
||||
request.ContactName,
|
||||
request.ContactPhone,
|
||||
request.Keyword,
|
||||
request.Page,
|
||||
request.PageSize,
|
||||
cancellationToken);
|
||||
|
||||
// 2. 分页
|
||||
var paged = tenants
|
||||
.Skip((request.Page - 1) * request.PageSize)
|
||||
.Take(request.PageSize)
|
||||
.ToList();
|
||||
|
||||
// 3. 映射 DTO(带订阅与认证)
|
||||
var result = new List<TenantDto>(paged.Count);
|
||||
foreach (var tenant in paged)
|
||||
// 2. (空行后) 无数据直接返回
|
||||
if (tenants.Count == 0)
|
||||
{
|
||||
var subscription = await tenantRepository.GetActiveSubscriptionAsync(tenant.Id, cancellationToken);
|
||||
var verification = await tenantRepository.GetVerificationProfileAsync(tenant.Id, cancellationToken);
|
||||
return new PagedResult<TenantDto>([], request.Page, request.PageSize, total);
|
||||
}
|
||||
|
||||
// 3. (空行后) 批量查询订阅与实名资料(避免 N+1)
|
||||
var tenantIds = tenants.Select(x => x.Id).ToArray();
|
||||
var subscriptionsTask = tenantRepository.GetSubscriptionsAsync(tenantIds, cancellationToken);
|
||||
var verificationsTask = tenantRepository.GetVerificationProfilesAsync(tenantIds, cancellationToken);
|
||||
await Task.WhenAll(subscriptionsTask, verificationsTask);
|
||||
|
||||
// 4. (空行后) 构建订阅与实名资料映射
|
||||
var subscriptionByTenantId = (await subscriptionsTask)
|
||||
.GroupBy(x => x.TenantId)
|
||||
.ToDictionary(x => x.Key, x => x.FirstOrDefault());
|
||||
var verificationByTenantId = (await verificationsTask)
|
||||
.ToDictionary(x => x.TenantId);
|
||||
|
||||
// 5. (空行后) 映射 DTO(带订阅与认证)
|
||||
var result = new List<TenantDto>(tenants.Count);
|
||||
foreach (var tenant in tenants)
|
||||
{
|
||||
subscriptionByTenantId.TryGetValue(tenant.Id, out var subscription);
|
||||
verificationByTenantId.TryGetValue(tenant.Id, out var verification);
|
||||
result.Add(TenantMapping.ToDto(tenant, subscription, verification));
|
||||
}
|
||||
|
||||
// 4. 返回分页结果
|
||||
// 6. (空行后) 返回分页结果
|
||||
return new PagedResult<TenantDto>(result, request.Page, request.PageSize, total);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,45 @@ namespace TakeoutSaaS.Application.App.Tenants.Queries;
|
||||
/// <summary>
|
||||
/// 租户分页查询。
|
||||
/// </summary>
|
||||
public sealed record SearchTenantsQuery(
|
||||
TenantStatus? Status,
|
||||
string? Keyword,
|
||||
int Page = 1,
|
||||
int PageSize = 20) : IRequest<PagedResult<TenantDto>>;
|
||||
public sealed record SearchTenantsQuery : IRequest<PagedResult<TenantDto>>
|
||||
{
|
||||
/// <summary>
|
||||
/// 租户状态(精确匹配)。
|
||||
/// </summary>
|
||||
public TenantStatus? Status { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 实名认证状态(精确匹配)。
|
||||
/// </summary>
|
||||
public TenantVerificationStatus? VerificationStatus { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 租户名称(模糊匹配)。
|
||||
/// </summary>
|
||||
public string? Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 联系人姓名(模糊匹配)。
|
||||
/// </summary>
|
||||
public string? ContactName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 联系电话(模糊匹配)。
|
||||
/// </summary>
|
||||
public string? ContactPhone { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 兼容关键词:按“名称/编码/联系人/电话”做模糊匹配。
|
||||
/// </summary>
|
||||
public string? Keyword { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 页码(从 1 开始)。
|
||||
/// </summary>
|
||||
public int Page { get; init; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// 每页大小。
|
||||
/// </summary>
|
||||
public int PageSize { get; init; } = 20;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
using FluentValidation;
|
||||
using TakeoutSaaS.Application.App.Tenants.Queries;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Tenants.Validators;
|
||||
|
||||
/// <summary>
|
||||
/// 租户列表查询验证器。
|
||||
/// </summary>
|
||||
public sealed class SearchTenantsQueryValidator : AbstractValidator<SearchTenantsQuery>
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化验证规则。
|
||||
/// </summary>
|
||||
public SearchTenantsQueryValidator()
|
||||
{
|
||||
RuleFor(x => x.Page).GreaterThan(0);
|
||||
RuleFor(x => x.PageSize).InclusiveBetween(1, 200);
|
||||
RuleFor(x => x.Keyword).MaximumLength(128);
|
||||
RuleFor(x => x.Name).MaximumLength(128);
|
||||
RuleFor(x => x.ContactName).MaximumLength(64);
|
||||
RuleFor(x => x.ContactPhone).MaximumLength(32);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user