feat: 租户列表 API 支持按状态过滤

- ListTenantsQuery 添加 Status 可选参数
- ITenantRepository.GetAllAsync 添加 status 参数
- EfTenantRepository 实现状态过滤逻辑
- TenantsController.List 添加 status 查询参数

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
MSuMshk
2026-02-02 20:46:40 +08:00
parent 874bd799e7
commit abeb352b04
5 changed files with 25 additions and 6 deletions

View File

@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc;
using TakeoutSaaS.Application.App.Tenants.Commands;
using TakeoutSaaS.Application.App.Tenants.Contracts;
using TakeoutSaaS.Application.App.Tenants.Queries;
using TakeoutSaaS.Domain.Tenants.Enums;
using TakeoutSaaS.Module.Authorization.Attributes;
using TakeoutSaaS.Shared.Abstractions.Results;
using TakeoutSaaS.Shared.Web.Api;
@@ -22,6 +23,7 @@ public sealed class TenantsController(IMediator mediator) : BaseApiController
/// 获取租户列表(用于下拉选择器)。
/// </summary>
/// <param name="keyword">关键字(租户名称/编码)。</param>
/// <param name="status">租户状态过滤(可选)。</param>
/// <param name="page">页码(从 1 开始)。</param>
/// <param name="pageSize">每页条数。</param>
/// <param name="cancellationToken">取消标记。</param>
@@ -31,6 +33,7 @@ public sealed class TenantsController(IMediator mediator) : BaseApiController
[ProducesResponseType(typeof(ApiResponse<PagedResult<TenantListItemDto>>), StatusCodes.Status200OK)]
public async Task<ApiResponse<PagedResult<TenantListItemDto>>> List(
[FromQuery] string? keyword,
[FromQuery] TenantStatus? status,
[FromQuery] int page = 1,
[FromQuery] int pageSize = 20,
CancellationToken cancellationToken = default)
@@ -39,6 +42,7 @@ public sealed class TenantsController(IMediator mediator) : BaseApiController
var query = new ListTenantsQuery
{
Keyword = keyword,
Status = status,
Page = page,
PageSize = pageSize
};

View File

@@ -16,8 +16,8 @@ public sealed class ListTenantsQueryHandler(ITenantRepository tenantRepository)
/// <inheritdoc />
public async Task<PagedResult<TenantListItemDto>> Handle(ListTenantsQuery request, CancellationToken cancellationToken)
{
// 1. 查询租户列表
var tenants = await tenantRepository.GetAllAsync(request.Keyword, cancellationToken);
// 1. 查询租户列表(支持状态过滤)
var tenants = await tenantRepository.GetAllAsync(request.Keyword, request.Status, cancellationToken);
// 2. 计算分页参数
var totalCount = tenants.Count;

View File

@@ -1,5 +1,6 @@
using MediatR;
using TakeoutSaaS.Application.App.Tenants.Contracts;
using TakeoutSaaS.Domain.Tenants.Enums;
using TakeoutSaaS.Shared.Abstractions.Results;
namespace TakeoutSaaS.Application.App.Tenants.Queries;
@@ -14,6 +15,11 @@ public sealed record ListTenantsQuery : IRequest<PagedResult<TenantListItemDto>>
/// </summary>
public string? Keyword { get; init; }
/// <summary>
/// 租户状态过滤(可选)。
/// </summary>
public TenantStatus? Status { get; init; }
/// <summary>
/// 页码(从 1 开始)。
/// </summary>

View File

@@ -1,5 +1,6 @@
using TakeoutSaaS.Domain.Billings.Entities;
using TakeoutSaaS.Domain.Tenants.Entities;
using TakeoutSaaS.Domain.Tenants.Enums;
namespace TakeoutSaaS.Domain.Tenants.Repositories;
@@ -37,9 +38,10 @@ public interface ITenantRepository
/// 获取所有租户列表(用于下拉选择器)。
/// </summary>
/// <param name="keyword">关键字(租户名称/编码)。</param>
/// <param name="status">租户状态过滤(可选)。</param>
/// <param name="cancellationToken">取消标记。</param>
/// <returns>租户列表。</returns>
Task<IReadOnlyList<Tenant>> GetAllAsync(string? keyword, CancellationToken cancellationToken = default);
Task<IReadOnlyList<Tenant>> GetAllAsync(string? keyword, TenantStatus? status = null, CancellationToken cancellationToken = default);
/// <summary>
/// 获取租户详情(包含认证、订阅、套餐信息)。

View File

@@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore;
using TakeoutSaaS.Domain.Billings.Entities;
using TakeoutSaaS.Domain.Tenants.Entities;
using TakeoutSaaS.Domain.Tenants.Enums;
using TakeoutSaaS.Domain.Tenants.Repositories;
using TakeoutSaaS.Infrastructure.App.Persistence;
@@ -55,21 +56,27 @@ public sealed class EfTenantRepository(TakeoutAdminDbContext context) : ITenantR
}
/// <inheritdoc />
public async Task<IReadOnlyList<Tenant>> GetAllAsync(string? keyword, CancellationToken cancellationToken = default)
public async Task<IReadOnlyList<Tenant>> GetAllAsync(string? keyword, TenantStatus? status = null, CancellationToken cancellationToken = default)
{
// 1. 构建基础查询
var query = context.Tenants
.AsNoTracking()
.Where(x => x.DeletedAt == null);
// 2. 应用关键字过滤
// 2. 应用状态过滤
if (status.HasValue)
{
query = query.Where(x => x.Status == status.Value);
}
// 3. 应用关键字过滤
if (!string.IsNullOrWhiteSpace(keyword))
{
var normalized = keyword.Trim();
query = query.Where(x => x.Name.Contains(normalized) || x.Code.Contains(normalized));
}
// 3. 返回列表
// 4. 返回列表
return await query.OrderBy(x => x.Code).ToListAsync(cancellationToken);
}