fix: 自助注册回填主管理员
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
using TakeoutSaaS.Application.App.Tenants.Commands;
|
using TakeoutSaaS.Application.App.Tenants.Commands;
|
||||||
using TakeoutSaaS.Application.Identity.Abstractions;
|
using TakeoutSaaS.Application.Identity.Abstractions;
|
||||||
|
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||||
using TakeoutSaaS.Domain.Tenants.Entities;
|
using TakeoutSaaS.Domain.Tenants.Entities;
|
||||||
using TakeoutSaaS.Domain.Tenants.Enums;
|
using TakeoutSaaS.Domain.Tenants.Enums;
|
||||||
using TakeoutSaaS.Domain.Tenants.Repositories;
|
using TakeoutSaaS.Domain.Tenants.Repositories;
|
||||||
@@ -17,6 +18,8 @@ namespace TakeoutSaaS.Application.App.Tenants.Handlers;
|
|||||||
public sealed class CreateTenantAdminResetLinkTokenCommandHandler(
|
public sealed class CreateTenantAdminResetLinkTokenCommandHandler(
|
||||||
ITenantRepository tenantRepository,
|
ITenantRepository tenantRepository,
|
||||||
ITenantProvider tenantProvider,
|
ITenantProvider tenantProvider,
|
||||||
|
ITenantContextAccessor tenantContextAccessor,
|
||||||
|
IIdentityUserRepository identityUserRepository,
|
||||||
ICurrentUserAccessor currentUserAccessor,
|
ICurrentUserAccessor currentUserAccessor,
|
||||||
IAdminAuthService adminAuthService,
|
IAdminAuthService adminAuthService,
|
||||||
IAdminPasswordResetTokenStore tokenStore)
|
IAdminPasswordResetTokenStore tokenStore)
|
||||||
@@ -38,9 +41,28 @@ public sealed class CreateTenantAdminResetLinkTokenCommandHandler(
|
|||||||
var tenant = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken)
|
var tenant = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken)
|
||||||
?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在");
|
?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在");
|
||||||
|
|
||||||
|
// 2.1 (空行后) 若缺少主管理员则自动回填(兼容历史数据)
|
||||||
if (!tenant.PrimaryOwnerUserId.HasValue || tenant.PrimaryOwnerUserId.Value == 0)
|
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 小时有效)
|
// 3. (空行后) 签发一次性重置令牌(默认 24 小时有效)
|
||||||
@@ -70,4 +92,3 @@ public sealed class CreateTenantAdminResetLinkTokenCommandHandler(
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using MediatR;
|
|||||||
using TakeoutSaaS.Application.App.Tenants.Commands;
|
using TakeoutSaaS.Application.App.Tenants.Commands;
|
||||||
using TakeoutSaaS.Application.Identity.Abstractions;
|
using TakeoutSaaS.Application.Identity.Abstractions;
|
||||||
using TakeoutSaaS.Application.Identity.Contracts;
|
using TakeoutSaaS.Application.Identity.Contracts;
|
||||||
|
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||||
using TakeoutSaaS.Domain.Tenants.Entities;
|
using TakeoutSaaS.Domain.Tenants.Entities;
|
||||||
using TakeoutSaaS.Domain.Tenants.Enums;
|
using TakeoutSaaS.Domain.Tenants.Enums;
|
||||||
using TakeoutSaaS.Domain.Tenants.Repositories;
|
using TakeoutSaaS.Domain.Tenants.Repositories;
|
||||||
@@ -19,6 +20,7 @@ public sealed class ImpersonateTenantCommandHandler(
|
|||||||
ITenantRepository tenantRepository,
|
ITenantRepository tenantRepository,
|
||||||
ITenantProvider tenantProvider,
|
ITenantProvider tenantProvider,
|
||||||
ITenantContextAccessor tenantContextAccessor,
|
ITenantContextAccessor tenantContextAccessor,
|
||||||
|
IIdentityUserRepository identityUserRepository,
|
||||||
ICurrentUserAccessor currentUserAccessor,
|
ICurrentUserAccessor currentUserAccessor,
|
||||||
IAdminAuthService adminAuthService,
|
IAdminAuthService adminAuthService,
|
||||||
IJwtTokenService jwtTokenService)
|
IJwtTokenService jwtTokenService)
|
||||||
@@ -46,9 +48,28 @@ public sealed class ImpersonateTenantCommandHandler(
|
|||||||
var tenant = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken)
|
var tenant = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken)
|
||||||
?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在");
|
?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在");
|
||||||
|
|
||||||
|
// 2.1 (空行后) 若缺少主管理员则自动回填(兼容历史数据)
|
||||||
if (!tenant.PrimaryOwnerUserId.HasValue || tenant.PrimaryOwnerUserId.Value == 0)
|
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. (空行后) 进入目标租户上下文以读取租户内用户(避免多租户查询过滤导致找不到用户)
|
// 3. (空行后) 进入目标租户上下文以读取租户内用户(避免多租户查询过滤导致找不到用户)
|
||||||
|
|||||||
@@ -95,6 +95,11 @@ public sealed class SelfRegisterTenantCommandHandler(
|
|||||||
await identityUserRepository.AddAsync(adminUser, cancellationToken);
|
await identityUserRepository.AddAsync(adminUser, cancellationToken);
|
||||||
await identityUserRepository.SaveChangesAsync(cancellationToken);
|
await identityUserRepository.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
// 7.1 (空行后) 回填主管理员标识,确保后续伪装登录/重置管理员等能力可用
|
||||||
|
tenant.PrimaryOwnerUserId = adminUser.Id;
|
||||||
|
await tenantRepository.UpdateTenantAsync(tenant, cancellationToken);
|
||||||
|
await tenantRepository.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
// 8. 初始化租户管理员角色模板
|
// 8. 初始化租户管理员角色模板
|
||||||
await mediator.Send(new InitializeRoleTemplatesCommand
|
await mediator.Send(new InitializeRoleTemplatesCommand
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user