fix: 统一逐租户上下文执行
This commit is contained in:
@@ -49,15 +49,14 @@ public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIde
|
||||
/// <param name="excludeUserId">排除的用户 ID。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>存在返回 true。</returns>
|
||||
public Task<bool> ExistsByAccountAsync(long tenantId, string account, long? excludeUserId = null, CancellationToken cancellationToken = default)
|
||||
public async Task<bool> ExistsByAccountAsync(long tenantId, string account, long? excludeUserId = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. 标准化账号
|
||||
var normalized = account.Trim();
|
||||
|
||||
// 2. 构建查询(包含已删除数据)
|
||||
var query = dbContext.IdentityUsers
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
// 2. 构建查询(包含已删除数据,但不放开租户过滤)
|
||||
using var disableSoftDeleteScope = dbContext.DisableSoftDeleteFilter();
|
||||
var query = dbContext.IdentityUsers.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.Account == normalized);
|
||||
|
||||
if (excludeUserId.HasValue)
|
||||
@@ -66,7 +65,7 @@ public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIde
|
||||
}
|
||||
|
||||
// 3. 返回是否存在
|
||||
return query.AnyAsync(cancellationToken);
|
||||
return await query.AnyAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -77,15 +76,14 @@ public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIde
|
||||
/// <param name="excludeUserId">排除的用户 ID。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>存在返回 true。</returns>
|
||||
public Task<bool> ExistsByPhoneAsync(long tenantId, string phone, long? excludeUserId = null, CancellationToken cancellationToken = default)
|
||||
public async Task<bool> ExistsByPhoneAsync(long tenantId, string phone, long? excludeUserId = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. 标准化手机号
|
||||
var normalized = phone.Trim();
|
||||
|
||||
// 2. 构建查询(包含已删除数据)
|
||||
var query = dbContext.IdentityUsers
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
// 2. 构建查询(包含已删除数据,但不放开租户过滤)
|
||||
using var disableSoftDeleteScope = dbContext.DisableSoftDeleteFilter();
|
||||
var query = dbContext.IdentityUsers.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.Phone == normalized);
|
||||
|
||||
if (excludeUserId.HasValue)
|
||||
@@ -94,7 +92,7 @@ public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIde
|
||||
}
|
||||
|
||||
// 3. 返回是否存在
|
||||
return query.AnyAsync(cancellationToken);
|
||||
return await query.AnyAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -105,15 +103,14 @@ public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIde
|
||||
/// <param name="excludeUserId">排除的用户 ID。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>存在返回 true。</returns>
|
||||
public Task<bool> ExistsByEmailAsync(long tenantId, string email, long? excludeUserId = null, CancellationToken cancellationToken = default)
|
||||
public async Task<bool> ExistsByEmailAsync(long tenantId, string email, long? excludeUserId = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. 标准化邮箱
|
||||
var normalized = email.Trim();
|
||||
|
||||
// 2. 构建查询(包含已删除数据)
|
||||
var query = dbContext.IdentityUsers
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
// 2. 构建查询(包含已删除数据,但不放开租户过滤)
|
||||
using var disableSoftDeleteScope = dbContext.DisableSoftDeleteFilter();
|
||||
var query = dbContext.IdentityUsers.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.Email == normalized);
|
||||
|
||||
if (excludeUserId.HasValue)
|
||||
@@ -122,7 +119,7 @@ public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIde
|
||||
}
|
||||
|
||||
// 3. 返回是否存在
|
||||
return query.AnyAsync(cancellationToken);
|
||||
return await query.AnyAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -150,14 +147,14 @@ public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIde
|
||||
/// <param name="userId">用户 ID。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>后台用户或 null。</returns>
|
||||
public Task<IdentityUser?> GetForUpdateIncludingDeletedAsync(
|
||||
public async Task<IdentityUser?> GetForUpdateIncludingDeletedAsync(
|
||||
long tenantId,
|
||||
long userId,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. 构建查询(包含已删除数据,但强制租户隔离)
|
||||
return dbContext.IdentityUsers
|
||||
.IgnoreQueryFilters()
|
||||
using var disableSoftDeleteScope = dbContext.DisableSoftDeleteFilter();
|
||||
return await dbContext.IdentityUsers
|
||||
.Where(x => x.TenantId == tenantId)
|
||||
.FirstOrDefaultAsync(x => x.Id == userId, cancellationToken);
|
||||
}
|
||||
@@ -204,21 +201,14 @@ public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIde
|
||||
|
||||
var tenantId = filter.TenantId.Value;
|
||||
|
||||
using var disableSoftDeleteScope = filter.IncludeDeleted ? dbContext.DisableSoftDeleteFilter() : null;
|
||||
|
||||
// 1. 构建基础查询
|
||||
var query = dbContext.IdentityUsers.AsNoTracking();
|
||||
if (filter.IncludeDeleted)
|
||||
{
|
||||
query = query.IgnoreQueryFilters();
|
||||
}
|
||||
|
||||
// 2. 租户过滤(强制)
|
||||
query = query.Where(x => x.TenantId == tenantId);
|
||||
|
||||
if (!filter.IncludeDeleted)
|
||||
{
|
||||
query = query.Where(x => x.DeletedAt == null);
|
||||
}
|
||||
|
||||
// 3. 关键字筛选
|
||||
if (!string.IsNullOrWhiteSpace(filter.Keyword))
|
||||
{
|
||||
@@ -242,18 +232,9 @@ public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIde
|
||||
{
|
||||
var roleId = filter.RoleId.Value;
|
||||
var userRoles = dbContext.UserRoles.AsNoTracking();
|
||||
if (filter.IncludeDeleted)
|
||||
{
|
||||
userRoles = userRoles.IgnoreQueryFilters();
|
||||
}
|
||||
|
||||
userRoles = userRoles.Where(x => x.TenantId == tenantId);
|
||||
|
||||
if (!filter.IncludeDeleted)
|
||||
{
|
||||
userRoles = userRoles.Where(x => x.DeletedAt == null);
|
||||
}
|
||||
|
||||
query = query.Where(user => userRoles.Any(role => role.UserId == user.Id && role.RoleId == roleId));
|
||||
}
|
||||
|
||||
@@ -346,18 +327,10 @@ public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIde
|
||||
}
|
||||
|
||||
var query = dbContext.IdentityUsers.Where(x => ids.Contains(x.Id));
|
||||
if (includeDeleted)
|
||||
{
|
||||
query = query.IgnoreQueryFilters();
|
||||
}
|
||||
using var disableSoftDeleteScope = includeDeleted ? dbContext.DisableSoftDeleteFilter() : null;
|
||||
|
||||
query = query.Where(x => x.TenantId == tenantId);
|
||||
|
||||
if (!includeDeleted)
|
||||
{
|
||||
query = query.Where(x => x.DeletedAt == null);
|
||||
}
|
||||
|
||||
// 2. 返回列表
|
||||
return await query.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
@@ -18,9 +18,8 @@ public sealed class EfPermissionRepository(IdentityDbContext dbContext) : IPermi
|
||||
/// <returns>权限实体或 null。</returns>
|
||||
public Task<Permission?> FindByIdAsync(long permissionId, long tenantId, CancellationToken cancellationToken = default)
|
||||
=> dbContext.Permissions
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(x => x.Id == permissionId && x.DeletedAt == null, cancellationToken);
|
||||
.FirstOrDefaultAsync(x => x.Id == permissionId && x.TenantId == tenantId && x.DeletedAt == null, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// 根据权限编码获取权限。
|
||||
@@ -31,9 +30,8 @@ public sealed class EfPermissionRepository(IdentityDbContext dbContext) : IPermi
|
||||
/// <returns>权限实体或 null。</returns>
|
||||
public Task<Permission?> FindByCodeAsync(string code, long tenantId, CancellationToken cancellationToken = default)
|
||||
=> dbContext.Permissions
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(x => x.Code == code && x.DeletedAt == null, cancellationToken);
|
||||
.FirstOrDefaultAsync(x => x.Code == code && x.TenantId == tenantId && x.DeletedAt == null, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// 根据权限编码集合批量获取权限。
|
||||
@@ -51,11 +49,10 @@ public sealed class EfPermissionRepository(IdentityDbContext dbContext) : IPermi
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
|
||||
// 2. 读取全局权限(已固定)
|
||||
// 2. 读取租户权限
|
||||
return dbContext.Permissions
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
.Where(x => x.DeletedAt == null && normalizedCodes.Contains(x.Code))
|
||||
.Where(x => x.TenantId == tenantId && x.DeletedAt == null && normalizedCodes.Contains(x.Code))
|
||||
.ToListAsync(cancellationToken)
|
||||
.ContinueWith(t => (IReadOnlyList<Permission>)t.Result, cancellationToken);
|
||||
}
|
||||
@@ -69,9 +66,8 @@ public sealed class EfPermissionRepository(IdentityDbContext dbContext) : IPermi
|
||||
/// <returns>权限列表。</returns>
|
||||
public Task<IReadOnlyList<Permission>> GetByIdsAsync(long tenantId, IEnumerable<long> permissionIds, CancellationToken cancellationToken = default)
|
||||
=> dbContext.Permissions
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
.Where(x => x.DeletedAt == null && permissionIds.Contains(x.Id))
|
||||
.Where(x => x.TenantId == tenantId && x.DeletedAt == null && permissionIds.Contains(x.Id))
|
||||
.ToListAsync(cancellationToken)
|
||||
.ContinueWith(t => (IReadOnlyList<Permission>)t.Result, cancellationToken);
|
||||
|
||||
@@ -86,9 +82,8 @@ public sealed class EfPermissionRepository(IdentityDbContext dbContext) : IPermi
|
||||
{
|
||||
// 1. 构建基础查询
|
||||
var query = dbContext.Permissions
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
.Where(x => x.DeletedAt == null);
|
||||
.Where(x => x.TenantId == tenantId && x.DeletedAt == null);
|
||||
if (!string.IsNullOrWhiteSpace(keyword))
|
||||
{
|
||||
// 2. 追加关键字过滤
|
||||
@@ -139,7 +134,7 @@ public sealed class EfPermissionRepository(IdentityDbContext dbContext) : IPermi
|
||||
public async Task DeleteAsync(long permissionId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. 查询目标权限
|
||||
var entity = await dbContext.Permissions.FirstOrDefaultAsync(x => x.Id == permissionId, cancellationToken);
|
||||
var entity = await dbContext.Permissions.FirstOrDefaultAsync(x => x.Id == permissionId && x.TenantId == tenantId, cancellationToken);
|
||||
if (entity != null)
|
||||
{
|
||||
// 2. 删除实体
|
||||
|
||||
@@ -16,13 +16,17 @@ public sealed class EfRolePermissionRepository(IdentityDbContext dbContext) : IR
|
||||
/// <param name="roleIds">角色 ID 集合。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>角色权限映射列表。</returns>
|
||||
public Task<IReadOnlyList<RolePermission>> GetByRoleIdsAsync(long tenantId, IEnumerable<long> roleIds, CancellationToken cancellationToken = default)
|
||||
=> dbContext.RolePermissions
|
||||
.IgnoreQueryFilters()
|
||||
public async Task<IReadOnlyList<RolePermission>> GetByRoleIdsAsync(long tenantId, IEnumerable<long> roleIds, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. 查询角色权限映射
|
||||
var mappings = await dbContext.RolePermissions
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.DeletedAt == null && roleIds.Contains(x.RoleId))
|
||||
.ToListAsync(cancellationToken)
|
||||
.ContinueWith(t => (IReadOnlyList<RolePermission>)t.Result, cancellationToken);
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// 2. (空行后) 返回只读列表
|
||||
return mappings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量新增角色权限。
|
||||
|
||||
@@ -18,7 +18,6 @@ public sealed class EfRoleRepository(IdentityDbContext dbContext) : IRoleReposit
|
||||
/// <returns>角色实体或 null。</returns>
|
||||
public Task<Role?> FindByIdAsync(long roleId, long tenantId, CancellationToken cancellationToken = default)
|
||||
=> dbContext.Roles
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(x => x.Id == roleId && x.TenantId == tenantId && x.DeletedAt == null, cancellationToken);
|
||||
|
||||
@@ -31,7 +30,6 @@ public sealed class EfRoleRepository(IdentityDbContext dbContext) : IRoleReposit
|
||||
/// <returns>角色实体或 null。</returns>
|
||||
public Task<Role?> FindByCodeAsync(string code, long tenantId, CancellationToken cancellationToken = default)
|
||||
=> dbContext.Roles
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(x => x.Code == code && x.TenantId == tenantId && x.DeletedAt == null, cancellationToken);
|
||||
|
||||
@@ -42,13 +40,17 @@ public sealed class EfRoleRepository(IdentityDbContext dbContext) : IRoleReposit
|
||||
/// <param name="roleIds">角色 ID 集合。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>角色列表。</returns>
|
||||
public Task<IReadOnlyList<Role>> GetByIdsAsync(long tenantId, IEnumerable<long> roleIds, CancellationToken cancellationToken = default)
|
||||
=> dbContext.Roles
|
||||
.IgnoreQueryFilters()
|
||||
public async Task<IReadOnlyList<Role>> GetByIdsAsync(long tenantId, IEnumerable<long> roleIds, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. 查询角色列表
|
||||
var roles = await dbContext.Roles
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && roleIds.Contains(x.Id) && x.DeletedAt == null)
|
||||
.ToListAsync(cancellationToken)
|
||||
.ContinueWith(t => (IReadOnlyList<Role>)t.Result, cancellationToken);
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// 2. (空行后) 返回只读列表
|
||||
return roles;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按关键字搜索角色。
|
||||
@@ -57,11 +59,10 @@ public sealed class EfRoleRepository(IdentityDbContext dbContext) : IRoleReposit
|
||||
/// <param name="keyword">搜索关键字。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>角色列表。</returns>
|
||||
public Task<IReadOnlyList<Role>> SearchAsync(long tenantId, string? keyword, CancellationToken cancellationToken = default)
|
||||
public async Task<IReadOnlyList<Role>> SearchAsync(long tenantId, string? keyword, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. 构建基础查询
|
||||
var query = dbContext.Roles
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.DeletedAt == null);
|
||||
if (!string.IsNullOrWhiteSpace(keyword))
|
||||
@@ -72,8 +73,10 @@ public sealed class EfRoleRepository(IdentityDbContext dbContext) : IRoleReposit
|
||||
}
|
||||
|
||||
// 3. 返回列表
|
||||
return query.ToListAsync(cancellationToken)
|
||||
.ContinueWith(t => (IReadOnlyList<Role>)t.Result, cancellationToken);
|
||||
var roles = await query.ToListAsync(cancellationToken);
|
||||
|
||||
// 4. (空行后) 返回只读列表
|
||||
return roles;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -16,13 +16,17 @@ public sealed class EfUserRoleRepository(IdentityDbContext dbContext) : IUserRol
|
||||
/// <param name="userIds">用户 ID 集合。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>用户角色映射列表。</returns>
|
||||
public Task<IReadOnlyList<UserRole>> GetByUserIdsAsync(long tenantId, IEnumerable<long> userIds, CancellationToken cancellationToken = default)
|
||||
=> dbContext.UserRoles
|
||||
.IgnoreQueryFilters()
|
||||
public async Task<IReadOnlyList<UserRole>> GetByUserIdsAsync(long tenantId, IEnumerable<long> userIds, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. 查询用户角色映射
|
||||
var mappings = await dbContext.UserRoles
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.DeletedAt == null && userIds.Contains(x.UserId))
|
||||
.ToListAsync(cancellationToken)
|
||||
.ContinueWith(t => (IReadOnlyList<UserRole>)t.Result, cancellationToken);
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// 2. (空行后) 返回只读列表
|
||||
return mappings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定用户的角色集合。
|
||||
@@ -31,13 +35,17 @@ public sealed class EfUserRoleRepository(IdentityDbContext dbContext) : IUserRol
|
||||
/// <param name="userId">用户 ID。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>用户角色列表。</returns>
|
||||
public Task<IReadOnlyList<UserRole>> GetByUserIdAsync(long tenantId, long userId, CancellationToken cancellationToken = default)
|
||||
=> dbContext.UserRoles
|
||||
.IgnoreQueryFilters()
|
||||
public async Task<IReadOnlyList<UserRole>> GetByUserIdAsync(long tenantId, long userId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. 查询用户角色映射
|
||||
var mappings = await dbContext.UserRoles
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.DeletedAt == null && x.UserId == userId)
|
||||
.ToListAsync(cancellationToken)
|
||||
.ContinueWith(t => (IReadOnlyList<UserRole>)t.Result, cancellationToken);
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// 2. (空行后) 返回只读列表
|
||||
return mappings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 替换指定用户的角色集合。
|
||||
@@ -56,8 +64,8 @@ public sealed class EfUserRoleRepository(IdentityDbContext dbContext) : IUserRol
|
||||
await using var trx = await dbContext.Database.BeginTransactionAsync(cancellationToken);
|
||||
|
||||
// 2. 读取当前角色映射
|
||||
using var disableSoftDeleteScope = dbContext.DisableSoftDeleteFilter();
|
||||
var existing = await dbContext.UserRoles
|
||||
.IgnoreQueryFilters()
|
||||
.Where(x => x.TenantId == tenantId && x.UserId == userId)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
@@ -111,7 +119,6 @@ public sealed class EfUserRoleRepository(IdentityDbContext dbContext) : IUserRol
|
||||
/// <returns>用户数量。</returns>
|
||||
public Task<int> CountUsersByRoleAsync(long tenantId, long roleId, CancellationToken cancellationToken = default)
|
||||
=> dbContext.UserRoles
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.DeletedAt == null && x.RoleId == roleId)
|
||||
.CountAsync(cancellationToken);
|
||||
|
||||
@@ -59,7 +59,7 @@ public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger
|
||||
foreach (var userOptions in options.Users)
|
||||
{
|
||||
// 6.1 进入租户作用域
|
||||
using var tenantScope = EnterTenantScope(tenantContextAccessor, userOptions.TenantId);
|
||||
using var tenantScope = tenantContextAccessor.EnterTenantScope(userOptions.TenantId, "admin-seed");
|
||||
// 6.2 查询账号并收集配置
|
||||
var user = await context.IdentityUsers.FirstOrDefaultAsync(x => x.Account == userOptions.Account, cancellationToken);
|
||||
var roles = NormalizeValues(userOptions.Roles);
|
||||
@@ -112,9 +112,8 @@ public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger
|
||||
});
|
||||
}
|
||||
|
||||
// 6.6 读取全局权限定义(固定权限,不再按租户生成)
|
||||
// 6.6 读取当前租户权限定义
|
||||
var existingPermissions = await context.Permissions
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
.Where(p => permissions.Contains(p.Code))
|
||||
.ToListAsync(cancellationToken);
|
||||
@@ -324,17 +323,4 @@ public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger
|
||||
.Where(v => !string.IsNullOrWhiteSpace(v))
|
||||
.Select(v => v.Trim())
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)];
|
||||
|
||||
private static IDisposable EnterTenantScope(ITenantContextAccessor accessor, long tenantId)
|
||||
{
|
||||
var previous = accessor.Current;
|
||||
accessor.Current = new TenantContext(tenantId, null, "admin-seed");
|
||||
return new Scope(() => accessor.Current = previous);
|
||||
}
|
||||
|
||||
private sealed class Scope(Action disposeAction) : IDisposable
|
||||
{
|
||||
private readonly Action _disposeAction = disposeAction;
|
||||
public void Dispose() => _disposeAction();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,14 @@ using Microsoft.EntityFrameworkCore;
|
||||
using TakeoutSaaS.Domain.Identity.Entities;
|
||||
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||
using TakeoutSaaS.Infrastructure.Identity.Persistence;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Infrastructure.Identity.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// 菜单仓储 EF 实现。
|
||||
/// </summary>
|
||||
public sealed class EfMenuRepository(IdentityDbContext dbContext) : IMenuRepository
|
||||
public sealed class EfMenuRepository(IdentityDbContext dbContext, ITenantContextAccessor tenantContextAccessor) : IMenuRepository
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<MenuDefinition>> GetByTenantAsync(long tenantId, CancellationToken cancellationToken = default)
|
||||
@@ -26,15 +27,17 @@ public sealed class EfMenuRepository(IdentityDbContext dbContext) : IMenuReposit
|
||||
}
|
||||
|
||||
// 2. (空行后) 回退系统默认菜单(TenantId=0)
|
||||
var systemMenus = await dbContext.MenuDefinitions
|
||||
.AsNoTracking()
|
||||
.IgnoreQueryFilters()
|
||||
.Where(x => x.TenantId == 0 && x.DeletedAt == null)
|
||||
.OrderBy(x => x.ParentId)
|
||||
.ThenBy(x => x.SortOrder)
|
||||
.ToListAsync(cancellationToken);
|
||||
using (tenantContextAccessor.EnterTenantScope(0, "menu"))
|
||||
{
|
||||
var systemMenus = await dbContext.MenuDefinitions
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == 0 && x.DeletedAt == null)
|
||||
.OrderBy(x => x.ParentId)
|
||||
.ThenBy(x => x.SortOrder)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return systemMenus;
|
||||
return systemMenus;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -52,10 +55,12 @@ public sealed class EfMenuRepository(IdentityDbContext dbContext) : IMenuReposit
|
||||
}
|
||||
|
||||
// 2. (空行后) 回退查系统默认菜单(TenantId=0)
|
||||
return await dbContext.MenuDefinitions
|
||||
.AsNoTracking()
|
||||
.IgnoreQueryFilters()
|
||||
.FirstOrDefaultAsync(x => x.Id == id && x.TenantId == 0 && x.DeletedAt == null, cancellationToken);
|
||||
using (tenantContextAccessor.EnterTenantScope(0, "menu"))
|
||||
{
|
||||
return await dbContext.MenuDefinitions
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(x => x.Id == id && x.TenantId == 0 && x.DeletedAt == null, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
Reference in New Issue
Block a user