diff --git a/src/Application/TakeoutSaaS.Application/Dictionary/Services/DictionaryAppService.cs b/src/Application/TakeoutSaaS.Application/Dictionary/Services/DictionaryAppService.cs index b657b92..4001b0a 100644 --- a/src/Application/TakeoutSaaS.Application/Dictionary/Services/DictionaryAppService.cs +++ b/src/Application/TakeoutSaaS.Application/Dictionary/Services/DictionaryAppService.cs @@ -327,7 +327,7 @@ public sealed class DictionaryAppService( var tenantId = tenantProvider.GetCurrentTenantId(); if (scope == DictionaryScope.System) { - EnsurePlatformTenant(tenantId); + EnsureSystemTenant(tenantId); return 0; } @@ -362,9 +362,9 @@ public sealed class DictionaryAppService( } } - private void EnsurePlatformTenant(long tenantId) + private void EnsureSystemTenant(long tenantId) { - // 1. (空行后) 系统字典只能在平台租户(TenantId=0)上下文中操作 + // 1. (空行后) 系统字典只能在系统租户(TenantId=0)上下文中操作 if (tenantId != 0) { throw new BusinessException(ErrorCodes.Forbidden, "租户端不允许操作系统字典"); diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/Tenant.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/Tenant.cs index d689c2b..caee976 100644 --- a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/Tenant.cs +++ b/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/Tenant.cs @@ -5,7 +5,7 @@ using TakeoutSaaS.Shared.Abstractions.Entities; namespace TakeoutSaaS.Domain.Tenants.Entities; /// -/// 平台租户信息,描述租户的生命周期与基础资料。 +/// 租户信息,描述租户的生命周期与基础资料。 /// public sealed class Tenant : AuditableEntityBase { diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/AppDataSeeder.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/AppDataSeeder.cs index 4bc9089..be264e3 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/AppDataSeeder.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/AppDataSeeder.cs @@ -39,7 +39,7 @@ public sealed class AppDataSeeder( var appDbContext = scope.ServiceProvider.GetRequiredService(); var dictionaryDbContext = scope.ServiceProvider.GetRequiredService(); - await EnsurePlatformTenantAsync(appDbContext, cancellationToken); + await EnsureSystemTenantAsync(appDbContext, cancellationToken); var defaultTenantId = await EnsureDefaultTenantAsync(appDbContext, cancellationToken); await EnsureDictionarySeedsAsync(dictionaryDbContext, defaultTenantId, cancellationToken); @@ -132,9 +132,9 @@ public sealed class AppDataSeeder( } /// - /// 确保平台租户存在。 + /// 确保系统租户存在(TenantId=0,用于系统级数据归属)。 /// - private async Task EnsurePlatformTenantAsync(TakeoutAppDbContext dbContext, CancellationToken cancellationToken) + private async Task EnsureSystemTenantAsync(TakeoutAppDbContext dbContext, CancellationToken cancellationToken) { var existingTenant = await dbContext.Tenants .IgnoreQueryFilters() @@ -142,20 +142,47 @@ public sealed class AppDataSeeder( if (existingTenant != null) { + // 1. (空行后) 若历史数据仍为 PLATFORM,则自动修正为 SYSTEM + var updated = false; + if (!string.Equals(existingTenant.Code, "SYSTEM", StringComparison.Ordinal)) + { + existingTenant.Code = "SYSTEM"; + updated = true; + } + + if (!string.Equals(existingTenant.Name, "System", StringComparison.Ordinal)) + { + existingTenant.Name = "System"; + updated = true; + } + + if (existingTenant.Status != TenantStatus.Active) + { + existingTenant.Status = TenantStatus.Active; + updated = true; + } + + if (updated) + { + dbContext.Tenants.Update(existingTenant); + await dbContext.SaveChangesAsync(cancellationToken); + logger.LogInformation("AppSeed 已更新系统租户 SYSTEM"); + } + return; } var tenant = new Tenant { Id = 0, - Code = "PLATFORM", - Name = "Platform", + Code = "SYSTEM", + Name = "System", Status = TenantStatus.Active }; await dbContext.Tenants.AddAsync(tenant, cancellationToken); await dbContext.SaveChangesAsync(cancellationToken); - logger.LogInformation("AppSeed 已创建平台租户 PLATFORM"); + logger.LogInformation("AppSeed 已创建系统租户 SYSTEM"); } /// diff --git a/src/Modules/TakeoutSaaS.Module.Tenancy/TenantResolutionMiddleware.cs b/src/Modules/TakeoutSaaS.Module.Tenancy/TenantResolutionMiddleware.cs index e686b7f..be230e0 100644 --- a/src/Modules/TakeoutSaaS.Module.Tenancy/TenantResolutionMiddleware.cs +++ b/src/Modules/TakeoutSaaS.Module.Tenancy/TenantResolutionMiddleware.cs @@ -89,18 +89,25 @@ public sealed class TenantResolutionMiddleware( private static TenantContext ResolveTenant(HttpContext context, TenantResolutionOptions options) { var request = context.Request; + var isAuthenticated = context.User?.Identity?.IsAuthenticated == true; // 1. Token Claim(已认证请求必须以 Claim 为准,避免 Header 覆盖导致跨租户访问) var claim = context.User?.FindFirst("tenant_id"); - if (claim != null && long.TryParse(claim.Value, out var claimTenant)) + if (claim != null && long.TryParse(claim.Value, out var claimTenant) && claimTenant > 0) { return new TenantContext(claimTenant, null, "claim:tenant_id"); } + // 1.1 (空行后) 已认证但缺少合法租户 Claim,则视为未解析(不允许 Header 覆盖) + if (isAuthenticated) + { + return TenantContext.Empty; + } // 2. Header 中的租户 ID if (!string.IsNullOrWhiteSpace(options.TenantIdHeaderName) && request.Headers.TryGetValue(options.TenantIdHeaderName, out var tenantHeader) && - long.TryParse(tenantHeader.FirstOrDefault(), out var headerTenantId)) + long.TryParse(tenantHeader.FirstOrDefault(), out var headerTenantId) && + headerTenantId > 0) { return new TenantContext(headerTenantId, null, $"header:{options.TenantIdHeaderName}"); } @@ -120,7 +127,7 @@ public sealed class TenantResolutionMiddleware( var host = request.Host.Host; if (!string.IsNullOrWhiteSpace(host)) { - if (options.DomainTenantMap.TryGetValue(host, out var tenantFromHost)) + if (options.DomainTenantMap.TryGetValue(host, out var tenantFromHost) && tenantFromHost > 0) { return new TenantContext(tenantFromHost, null, $"host:{host}"); } @@ -143,7 +150,7 @@ public sealed class TenantResolutionMiddleware( return false; } - return options.CodeTenantMap.TryGetValue(code, out tenantId); + return options.CodeTenantMap.TryGetValue(code, out tenantId) && tenantId > 0; } private static string? ResolveCodeFromHost(string host, string? rootDomain)