feat: tenants 列表支持名称/联系人/电话/认证状态过滤

This commit is contained in:
2025-12-14 16:12:25 +08:00
parent 456b575596
commit c5a3243bd8
5 changed files with 260 additions and 21 deletions

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}