feat: add public tenant packages listing and sort order
This commit is contained in:
@@ -34,6 +34,22 @@ public sealed class AuthController(IAdminAuthService authService) : BaseApiContr
|
||||
return ApiResponse<TokenResponse>.Ok(response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 免租户号登录(仅账号+密码)。
|
||||
/// </summary>
|
||||
/// <param name="request">登录请求。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>包含访问令牌与刷新令牌的响应。</returns>
|
||||
/// <remarks>用于前端简化登录,无需额外传递租户号。</remarks>
|
||||
[HttpPost("login/simple")]
|
||||
[AllowAnonymous]
|
||||
[ProducesResponseType(typeof(ApiResponse<TokenResponse>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<TokenResponse>> LoginSimple([FromBody] AdminLoginRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await authService.LoginAsync(request, cancellationToken);
|
||||
return ApiResponse<TokenResponse>.Ok(response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新 Token
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.RateLimiting;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using TakeoutSaaS.Application.App.Tenants.Dto;
|
||||
using TakeoutSaaS.Application.App.Tenants.Queries;
|
||||
using TakeoutSaaS.Shared.Abstractions.Results;
|
||||
using TakeoutSaaS.Shared.Web.Api;
|
||||
|
||||
namespace TakeoutSaaS.AdminApi.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// 公共租户套餐查询接口。
|
||||
/// </summary>
|
||||
[ApiVersion("1.0")]
|
||||
[AllowAnonymous]
|
||||
[EnableRateLimiting("public-self-service")]
|
||||
[Route("api/public/v{version:apiVersion}/tenant-packages")]
|
||||
public sealed class PublicTenantPackagesController(IMediator mediator) : BaseApiController
|
||||
{
|
||||
/// <summary>
|
||||
/// 分页获取已启用的租户套餐。
|
||||
/// </summary>
|
||||
/// <param name="query">分页参数。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>启用套餐的分页列表。</returns>
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(ApiResponse<PagedResult<TenantPackageDto>>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<PagedResult<TenantPackageDto>>> List(
|
||||
[FromQuery, Required] GetPublicTenantPackagesQuery query,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 执行查询
|
||||
var result = await mediator.Send(query, cancellationToken);
|
||||
// 2. 返回结果
|
||||
return ApiResponse<PagedResult<TenantPackageDto>>.Ok(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.RateLimiting;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using TakeoutSaaS.Application.App.Tenants.Commands;
|
||||
using TakeoutSaaS.Application.App.Tenants.Dto;
|
||||
using TakeoutSaaS.Application.App.Tenants.Queries;
|
||||
using TakeoutSaaS.Shared.Abstractions.Results;
|
||||
using TakeoutSaaS.Shared.Web.Api;
|
||||
|
||||
namespace TakeoutSaaS.AdminApi.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// 公域租户自助入住接口。
|
||||
/// </summary>
|
||||
[ApiVersion("1.0")]
|
||||
[AllowAnonymous]
|
||||
[EnableRateLimiting("public-self-service")]
|
||||
[Route("api/public/v{version:apiVersion}/tenants")]
|
||||
public sealed class PublicTenantsController(IMediator mediator) : BaseApiController
|
||||
{
|
||||
/// <summary>
|
||||
/// 自助注册租户并生成初始管理员。
|
||||
/// </summary>
|
||||
/// <param name="command">自助注册命令。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>注册结果(含临时密码)。</returns>
|
||||
[HttpPost("self-register")]
|
||||
[ProducesResponseType(typeof(ApiResponse<SelfRegisterResultDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<SelfRegisterResultDto>> SelfRegister(
|
||||
[FromBody, Required] SelfRegisterTenantCommand command,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 执行自助注册
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
return ApiResponse<SelfRegisterResultDto>.Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自助提交或更新实名资料。
|
||||
/// </summary>
|
||||
/// <param name="tenantId">租户 ID。</param>
|
||||
/// <param name="command">实名资料。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>实名资料结果。</returns>
|
||||
[HttpPost("{tenantId:long}/verification")]
|
||||
[ProducesResponseType(typeof(ApiResponse<TenantVerificationDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<TenantVerificationDto>> SubmitVerification(
|
||||
long tenantId,
|
||||
[FromBody, Required] SubmitTenantVerificationCommand command,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 绑定租户 ID
|
||||
var merged = command with { TenantId = tenantId };
|
||||
// 2. 提交实名
|
||||
var result = await mediator.Send(merged, cancellationToken);
|
||||
return ApiResponse<TenantVerificationDto>.Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询租户入住进度。
|
||||
/// </summary>
|
||||
/// <param name="tenantId">租户 ID。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>入住进度。</returns>
|
||||
[HttpGet("{tenantId:long}/status")]
|
||||
[ProducesResponseType(typeof(ApiResponse<TenantProgressDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<TenantProgressDto>> Progress(long tenantId, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 查询进度
|
||||
var query = new GetTenantProgressQuery { TenantId = tenantId };
|
||||
var result = await mediator.Send(query, cancellationToken);
|
||||
return ApiResponse<TenantProgressDto>.Ok(result);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Cors.Infrastructure;
|
||||
using Microsoft.AspNetCore.RateLimiting;
|
||||
using OpenTelemetry.Metrics;
|
||||
using OpenTelemetry.Resources;
|
||||
using OpenTelemetry.Trace;
|
||||
using Serilog;
|
||||
using System.Threading.RateLimiting;
|
||||
using TakeoutSaaS.Application.App.Extensions;
|
||||
using TakeoutSaaS.Application.Identity.Extensions;
|
||||
using TakeoutSaaS.Application.Messaging.Extensions;
|
||||
@@ -75,6 +78,17 @@ builder.Services.AddMessagingModule(builder.Configuration);
|
||||
builder.Services.AddMessagingApplication();
|
||||
builder.Services.AddSchedulerModule(builder.Configuration);
|
||||
builder.Services.AddHealthChecks();
|
||||
builder.Services.AddRateLimiter(options =>
|
||||
{
|
||||
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
|
||||
options.AddFixedWindowLimiter("public-self-service", limiterOptions =>
|
||||
{
|
||||
limiterOptions.PermitLimit = 10;
|
||||
limiterOptions.Window = TimeSpan.FromMinutes(1);
|
||||
limiterOptions.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
|
||||
limiterOptions.QueueLimit = 2;
|
||||
});
|
||||
});
|
||||
|
||||
// 6. 配置 OpenTelemetry 采集
|
||||
var otelSection = builder.Configuration.GetSection("Otel");
|
||||
@@ -140,6 +154,7 @@ builder.Services.AddCors(options =>
|
||||
var app = builder.Build();
|
||||
app.UseCors("AdminApiCors");
|
||||
app.UseTenantResolution();
|
||||
app.UseRateLimiter();
|
||||
app.UseSharedWebCore();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
Reference in New Issue
Block a user