diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantAdminResetLinkTokenCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantAdminResetLinkTokenCommandHandler.cs index c3a00d3..ed3ea01 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantAdminResetLinkTokenCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantAdminResetLinkTokenCommandHandler.cs @@ -1,6 +1,7 @@ using MediatR; using TakeoutSaaS.Application.App.Tenants.Commands; using TakeoutSaaS.Application.Identity.Abstractions; +using TakeoutSaaS.Domain.Identity.Repositories; using TakeoutSaaS.Domain.Tenants.Entities; using TakeoutSaaS.Domain.Tenants.Enums; using TakeoutSaaS.Domain.Tenants.Repositories; @@ -17,6 +18,8 @@ namespace TakeoutSaaS.Application.App.Tenants.Handlers; public sealed class CreateTenantAdminResetLinkTokenCommandHandler( ITenantRepository tenantRepository, ITenantProvider tenantProvider, + ITenantContextAccessor tenantContextAccessor, + IIdentityUserRepository identityUserRepository, ICurrentUserAccessor currentUserAccessor, IAdminAuthService adminAuthService, IAdminPasswordResetTokenStore tokenStore) @@ -38,9 +41,28 @@ public sealed class CreateTenantAdminResetLinkTokenCommandHandler( var tenant = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在"); + // 2.1 (空行后) 若缺少主管理员则自动回填(兼容历史数据) if (!tenant.PrimaryOwnerUserId.HasValue || tenant.PrimaryOwnerUserId.Value == 0) { - throw new BusinessException(ErrorCodes.BadRequest, "该租户未配置主管理员账号,无法生成重置链接"); + var originalContextForFix = tenantContextAccessor.Current; + tenantContextAccessor.Current = new TenantContext(tenant.Id, tenant.Code, "admin:reset-link:fix-owner"); + try + { + var users = await identityUserRepository.SearchAsync(tenant.Id, keyword: null, cancellationToken); + var ownerCandidate = users.OrderBy(x => x.CreatedAt).FirstOrDefault(); + if (ownerCandidate == null) + { + throw new BusinessException(ErrorCodes.BadRequest, "该租户未配置主管理员账号,且未找到可用管理员账号"); + } + + tenant.PrimaryOwnerUserId = ownerCandidate.Id; + await tenantRepository.UpdateTenantAsync(tenant, cancellationToken); + await tenantRepository.SaveChangesAsync(cancellationToken); + } + finally + { + tenantContextAccessor.Current = originalContextForFix; + } } // 3. (空行后) 签发一次性重置令牌(默认 24 小时有效) @@ -70,4 +92,3 @@ public sealed class CreateTenantAdminResetLinkTokenCommandHandler( return token; } } - diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/ImpersonateTenantCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/ImpersonateTenantCommandHandler.cs index 68ce3b2..5df8441 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/ImpersonateTenantCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/ImpersonateTenantCommandHandler.cs @@ -2,6 +2,7 @@ using MediatR; using TakeoutSaaS.Application.App.Tenants.Commands; using TakeoutSaaS.Application.Identity.Abstractions; using TakeoutSaaS.Application.Identity.Contracts; +using TakeoutSaaS.Domain.Identity.Repositories; using TakeoutSaaS.Domain.Tenants.Entities; using TakeoutSaaS.Domain.Tenants.Enums; using TakeoutSaaS.Domain.Tenants.Repositories; @@ -19,6 +20,7 @@ public sealed class ImpersonateTenantCommandHandler( ITenantRepository tenantRepository, ITenantProvider tenantProvider, ITenantContextAccessor tenantContextAccessor, + IIdentityUserRepository identityUserRepository, ICurrentUserAccessor currentUserAccessor, IAdminAuthService adminAuthService, IJwtTokenService jwtTokenService) @@ -46,9 +48,28 @@ public sealed class ImpersonateTenantCommandHandler( var tenant = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在"); + // 2.1 (空行后) 若缺少主管理员则自动回填(兼容历史数据) if (!tenant.PrimaryOwnerUserId.HasValue || tenant.PrimaryOwnerUserId.Value == 0) { - throw new BusinessException(ErrorCodes.BadRequest, "该租户未配置主管理员账号,无法伪装登录"); + var originalContextForFix = tenantContextAccessor.Current; + tenantContextAccessor.Current = new TenantContext(tenant.Id, tenant.Code, "admin:impersonate:fix-owner"); + try + { + var users = await identityUserRepository.SearchAsync(tenant.Id, keyword: null, cancellationToken); + var ownerCandidate = users.OrderBy(x => x.CreatedAt).FirstOrDefault(); + if (ownerCandidate == null) + { + throw new BusinessException(ErrorCodes.BadRequest, "该租户未配置主管理员账号,且未找到可用管理员账号"); + } + + tenant.PrimaryOwnerUserId = ownerCandidate.Id; + await tenantRepository.UpdateTenantAsync(tenant, cancellationToken); + await tenantRepository.SaveChangesAsync(cancellationToken); + } + finally + { + tenantContextAccessor.Current = originalContextForFix; + } } // 3. (空行后) 进入目标租户上下文以读取租户内用户(避免多租户查询过滤导致找不到用户) diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SelfRegisterTenantCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SelfRegisterTenantCommandHandler.cs index 14c3503..e088811 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SelfRegisterTenantCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SelfRegisterTenantCommandHandler.cs @@ -95,6 +95,11 @@ public sealed class SelfRegisterTenantCommandHandler( await identityUserRepository.AddAsync(adminUser, cancellationToken); await identityUserRepository.SaveChangesAsync(cancellationToken); + // 7.1 (空行后) 回填主管理员标识,确保后续伪装登录/重置管理员等能力可用 + tenant.PrimaryOwnerUserId = adminUser.Id; + await tenantRepository.UpdateTenantAsync(tenant, cancellationToken); + await tenantRepository.SaveChangesAsync(cancellationToken); + // 8. 初始化租户管理员角色模板 await mediator.Send(new InitializeRoleTemplatesCommand {