Files
tiku-backend.net/Tiku.Api/Controllers/TenantPublicController.cs
xiong c497a3ca8d
Some checks failed
ci / release-gate (push) Has been cancelled
清理代码
2026-08-03 12:31:39 +08:00

129 lines
4.8 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Tiku.Api.Contracts;
using Tiku.Application.Security;
using Tiku.Application.Tenancy;
using Tiku.Domain.Tenancy;
namespace Tiku.Api.Controllers;
[ApiController]
[Tags("租户端-公开配置")]
[AllowAnonymous]
[Produces("application/json")]
[Route("api/public/tenant")]
public sealed class TenantPublicController(
ITenantContext tenantContext,
ITenantContextInitializer tenantContextInitializer,
ITenantDirectory tenantDirectory,
IPublicTenantConfigurationQuery publicConfiguration) : ControllerBase
{
[HttpGet("resolve")]
[EndpointSummary("解析当前租户")]
[EndpointDescription("根据访问域名、host 参数、x-tenant-code 或 tenantCode 查询启用中的租户及公开品牌配置。")]
[ProducesResponseType<TenantResolveResponseDto>(StatusCodes.Status200OK)]
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
public async Task<ActionResult<TenantResolveResponseDto>> Resolve(
[FromQuery] TenantResolveQueryDto query,
CancellationToken cancellationToken)
{
var tenantCode = NormalizeTenantCode(query.TenantCode ?? Request.Headers["x-tenant-code"].FirstOrDefault());
var host = NormalizeHost(query.Host ?? Request.Host.Host);
var directoryEntry = tenantContext.ResolutionSource == TenantResolutionSource.Host
? await tenantDirectory.FindByHostAsync(Request.Host.Host, cancellationToken)
: !string.IsNullOrWhiteSpace(tenantCode)
? await tenantDirectory.FindByCodeAsync(tenantCode, cancellationToken)
: string.IsNullOrWhiteSpace(host)
? null
: await tenantDirectory.FindByHostAsync(host, cancellationToken);
var tenant = directoryEntry is null
? null
: new TenantLookupResult(
directoryEntry.TenantId,
directoryEntry.TenantCode,
directoryEntry.Name,
directoryEntry.Status,
directoryEntry.Mode,
directoryEntry.Host);
if (tenant is null)
return NotFound(new ProblemDetails
{
Title = "Tenant was not found.",
Status = StatusCodes.Status404NotFound,
Detail = "No active tenant matches the supplied host or tenantCode."
});
tenantContextInitializer.Initialize(
tenant.Id,
tenant.Slug,
tenant.Host is null ? TenantResolutionSource.TenantCode : TenantResolutionSource.Host);
return Ok(await BuildResponseAsync(tenant, tenant.Host, cancellationToken));
}
[HttpGet("current-public")]
[EndpointSummary("获取公开租户配置")]
[EndpointDescription("公开页面使用的租户品牌、功能开关和 public config。和 resolve 返回结构一致。")]
[ProducesResponseType<TenantResolveResponseDto>(StatusCodes.Status200OK)]
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
public Task<ActionResult<TenantResolveResponseDto>> CurrentPublic(
[FromQuery] TenantResolveQueryDto query,
CancellationToken cancellationToken)
{
return Resolve(query, cancellationToken);
}
private async Task<TenantResolveResponseDto> BuildResponseAsync(
TenantLookupResult tenant,
string? host,
CancellationToken cancellationToken)
{
var configuration = await publicConfiguration.GetAsync(tenant.Id, cancellationToken);
return new TenantResolveResponseDto(
new PublicTenantDto(
tenant.Id,
tenant.Slug,
tenant.Name,
tenant.Status,
tenant.Mode,
host),
new PublicTenantBrandingDto(
configuration.BrandName,
configuration.ShortName,
configuration.Slogan,
configuration.LogoUrl,
configuration.FaviconUrl,
configuration.ServiceWechat,
configuration.ServiceAccountName,
configuration.Theme,
configuration.PublicAssets),
configuration.FeatureFlags,
configuration.AdminFeatureFlags,
configuration.PublicConfig);
}
private static string? NormalizeTenantCode(string? value)
{
return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
}
private static string? NormalizeHost(string? value)
{
if (string.IsNullOrWhiteSpace(value)) return null;
var host = value.Trim().ToLowerInvariant();
return host.Split(':', 2)[0];
}
private sealed record TenantLookupResult(
Guid Id,
string Slug,
string Name,
TenantStatus Status,
TenantMode Mode,
string? Host);
}