refactor: 管理端去租户过滤并Portal化RBAC菜单
This commit is contained in:
@@ -125,19 +125,6 @@ public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIde
|
||||
public Task<IdentityUser?> FindByIdAsync(long userId, CancellationToken cancellationToken = default)
|
||||
=> dbContext.IdentityUsers.AsNoTracking().FirstOrDefaultAsync(x => x.Id == userId, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// 根据 ID 获取后台用户(忽略租户过滤器,仅用于只读查询)。
|
||||
/// </summary>
|
||||
/// <param name="userId">用户 ID。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>后台用户或 null。</returns>
|
||||
public Task<IdentityUser?> FindByIdIgnoringTenantAsync(long userId, CancellationToken cancellationToken = default)
|
||||
=> dbContext.IdentityUsers
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
.Where(x => x.DeletedAt == null)
|
||||
.FirstOrDefaultAsync(x => x.Id == userId, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// 根据 ID 获取后台用户(用于更新,返回可跟踪实体)。
|
||||
/// </summary>
|
||||
@@ -147,43 +134,16 @@ public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIde
|
||||
public Task<IdentityUser?> GetForUpdateAsync(long userId, CancellationToken cancellationToken = default)
|
||||
=> dbContext.IdentityUsers.FirstOrDefaultAsync(x => x.Id == userId, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// 根据 ID 获取后台用户(用于更新,忽略租户过滤器)。
|
||||
/// </summary>
|
||||
/// <remarks>用于跨租户场景(如平台生成的重置密码链接)。</remarks>
|
||||
/// <param name="userId">用户 ID。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>后台用户或 null。</returns>
|
||||
public Task<IdentityUser?> GetForUpdateIgnoringTenantAsync(long userId, CancellationToken cancellationToken = default)
|
||||
=> dbContext.IdentityUsers
|
||||
.IgnoreQueryFilters()
|
||||
.Where(x => x.DeletedAt == null)
|
||||
.FirstOrDefaultAsync(x => x.Id == userId, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// 根据 ID 获取后台用户(用于更新,包含已删除数据)。
|
||||
/// </summary>
|
||||
/// <param name="tenantId">租户 ID。</param>
|
||||
/// <param name="userId">用户 ID。</param>
|
||||
/// <param name="ignoreTenantFilter">是否忽略租户过滤。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>后台用户或 null。</returns>
|
||||
public Task<IdentityUser?> GetForUpdateIncludingDeletedAsync(
|
||||
long tenantId,
|
||||
long userId,
|
||||
bool ignoreTenantFilter = false,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. 构建查询(包含已删除数据)
|
||||
var query = dbContext.IdentityUsers.IgnoreQueryFilters();
|
||||
if (!ignoreTenantFilter)
|
||||
{
|
||||
query = query.Where(x => x.TenantId == tenantId);
|
||||
}
|
||||
|
||||
// 2. 返回实体
|
||||
return query.FirstOrDefaultAsync(x => x.Id == userId, cancellationToken);
|
||||
}
|
||||
public Task<IdentityUser?> GetForUpdateIncludingDeletedAsync(long userId, CancellationToken cancellationToken = default)
|
||||
=> dbContext.IdentityUsers
|
||||
.IgnoreQueryFilters()
|
||||
.FirstOrDefaultAsync(x => x.Id == userId, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// 按租户与关键字搜索后台用户(只读)。
|
||||
@@ -214,40 +174,28 @@ public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIde
|
||||
/// 分页查询后台用户列表。
|
||||
/// </summary>
|
||||
/// <param name="filter">查询过滤条件。</param>
|
||||
/// <param name="ignoreTenantFilter">是否忽略租户过滤器。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>分页结果。</returns>
|
||||
public async Task<(IReadOnlyList<IdentityUser> Items, int Total)> SearchPagedAsync(
|
||||
IdentityUserSearchFilter filter,
|
||||
bool ignoreTenantFilter = false,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. 构建基础查询
|
||||
var query = dbContext.IdentityUsers.AsNoTracking();
|
||||
if (ignoreTenantFilter || filter.IncludeDeleted)
|
||||
|
||||
// 2. (空行后) 包含软删除数据时忽略全局过滤
|
||||
if (filter.IncludeDeleted)
|
||||
{
|
||||
query = query.IgnoreQueryFilters();
|
||||
}
|
||||
|
||||
// 2. 租户过滤
|
||||
if (!ignoreTenantFilter)
|
||||
{
|
||||
if (filter.TenantId.HasValue && filter.TenantId.Value != 0)
|
||||
{
|
||||
query = query.Where(x => x.TenantId == filter.TenantId.Value);
|
||||
}
|
||||
}
|
||||
else if (filter.TenantId.HasValue && filter.TenantId.Value != 0)
|
||||
// 3. (空行后) 可选租户过滤
|
||||
if (filter.TenantId.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.TenantId == filter.TenantId.Value);
|
||||
}
|
||||
|
||||
if (!filter.IncludeDeleted)
|
||||
{
|
||||
query = query.Where(x => x.DeletedAt == null);
|
||||
}
|
||||
|
||||
// 3. 关键字筛选
|
||||
// 4. (空行后) 关键字筛选
|
||||
if (!string.IsNullOrWhiteSpace(filter.Keyword))
|
||||
{
|
||||
var normalized = filter.Keyword.Trim();
|
||||
@@ -259,43 +207,35 @@ public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIde
|
||||
|| (x.Email != null && EF.Functions.ILike(x.Email, likeValue)));
|
||||
}
|
||||
|
||||
// 4. 状态过滤
|
||||
// 5. (空行后) 状态过滤
|
||||
if (filter.Status.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.Status == filter.Status.Value);
|
||||
}
|
||||
|
||||
// 5. 角色过滤
|
||||
// 6. (空行后) 角色过滤
|
||||
if (filter.RoleId.HasValue)
|
||||
{
|
||||
var roleId = filter.RoleId.Value;
|
||||
var userRoles = dbContext.UserRoles.AsNoTracking();
|
||||
if (ignoreTenantFilter || filter.IncludeDeleted)
|
||||
|
||||
// 6.1 包含软删除数据时忽略全局过滤
|
||||
if (filter.IncludeDeleted)
|
||||
{
|
||||
userRoles = userRoles.IgnoreQueryFilters();
|
||||
}
|
||||
|
||||
if (!ignoreTenantFilter)
|
||||
{
|
||||
if (filter.TenantId.HasValue && filter.TenantId.Value != 0)
|
||||
{
|
||||
userRoles = userRoles.Where(x => x.TenantId == filter.TenantId.Value);
|
||||
}
|
||||
}
|
||||
else if (filter.TenantId.HasValue && filter.TenantId.Value != 0)
|
||||
// 6.2 (空行后) 可选租户过滤
|
||||
if (filter.TenantId.HasValue)
|
||||
{
|
||||
userRoles = userRoles.Where(x => x.TenantId == filter.TenantId.Value);
|
||||
}
|
||||
|
||||
if (!filter.IncludeDeleted)
|
||||
{
|
||||
userRoles = userRoles.Where(x => x.DeletedAt == null);
|
||||
}
|
||||
|
||||
// 6.3 (空行后) 用户角色关联过滤
|
||||
query = query.Where(user => userRoles.Any(role => role.UserId == user.Id && role.RoleId == roleId));
|
||||
}
|
||||
|
||||
// 6. 时间范围过滤
|
||||
// 7. (空行后) 时间范围过滤
|
||||
if (filter.CreatedAtFrom.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.CreatedAt >= filter.CreatedAtFrom.Value);
|
||||
@@ -316,7 +256,7 @@ public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIde
|
||||
query = query.Where(x => x.LastLoginAt <= filter.LastLoginTo.Value);
|
||||
}
|
||||
|
||||
// 7. 排序
|
||||
// 8. (空行后) 排序
|
||||
var sorted = filter.SortBy?.ToLowerInvariant() switch
|
||||
{
|
||||
"account" => filter.SortDescending
|
||||
@@ -336,7 +276,7 @@ public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIde
|
||||
: query.OrderBy(x => x.CreatedAt)
|
||||
};
|
||||
|
||||
// 8. 分页
|
||||
// 9. (空行后) 分页
|
||||
var page = filter.Page <= 0 ? 1 : filter.Page;
|
||||
var pageSize = filter.PageSize <= 0 ? 20 : filter.PageSize;
|
||||
var total = await sorted.CountAsync(cancellationToken);
|
||||
@@ -355,11 +295,21 @@ public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIde
|
||||
/// <param name="userIds">用户 ID 集合。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>后台用户列表。</returns>
|
||||
public Task<IReadOnlyList<IdentityUser>> GetByIdsAsync(long tenantId, IEnumerable<long> userIds, CancellationToken cancellationToken = default)
|
||||
=> dbContext.IdentityUsers.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && userIds.Contains(x.Id))
|
||||
.ToListAsync(cancellationToken)
|
||||
.ContinueWith(t => (IReadOnlyList<IdentityUser>)t.Result, cancellationToken);
|
||||
public async Task<IReadOnlyList<IdentityUser>> GetByIdsAsync(long tenantId, IEnumerable<long> userIds, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. 去重并快速返回空集合
|
||||
var ids = userIds.Distinct().ToArray();
|
||||
if (ids.Length == 0)
|
||||
{
|
||||
return Array.Empty<IdentityUser>();
|
||||
}
|
||||
|
||||
// 2. (空行后) 查询并返回列表
|
||||
return await dbContext.IdentityUsers
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && ids.Contains(x.Id))
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量获取后台用户(可用于更新,支持包含已删除数据)。
|
||||
@@ -367,42 +317,33 @@ public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIde
|
||||
/// <param name="tenantId">租户 ID。</param>
|
||||
/// <param name="userIds">用户 ID 集合。</param>
|
||||
/// <param name="includeDeleted">是否包含已删除数据。</param>
|
||||
/// <param name="ignoreTenantFilter">是否忽略租户过滤器。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>后台用户列表。</returns>
|
||||
public Task<IReadOnlyList<IdentityUser>> GetForUpdateByIdsAsync(
|
||||
public async Task<IReadOnlyList<IdentityUser>> GetForUpdateByIdsAsync(
|
||||
long tenantId,
|
||||
IEnumerable<long> userIds,
|
||||
bool includeDeleted,
|
||||
bool ignoreTenantFilter = false,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. 构建基础查询
|
||||
// 1. 去重并快速返回空集合
|
||||
var ids = userIds.Distinct().ToArray();
|
||||
if (ids.Length == 0)
|
||||
{
|
||||
return Task.FromResult<IReadOnlyList<IdentityUser>>(Array.Empty<IdentityUser>());
|
||||
return Array.Empty<IdentityUser>();
|
||||
}
|
||||
|
||||
var query = dbContext.IdentityUsers.Where(x => ids.Contains(x.Id));
|
||||
if (ignoreTenantFilter || includeDeleted)
|
||||
// 2. (空行后) 构建查询
|
||||
var query = dbContext.IdentityUsers
|
||||
.Where(x => x.TenantId == tenantId && ids.Contains(x.Id));
|
||||
|
||||
// 3. (空行后) 包含软删除数据时忽略全局过滤
|
||||
if (includeDeleted)
|
||||
{
|
||||
query = query.IgnoreQueryFilters();
|
||||
}
|
||||
|
||||
if (!ignoreTenantFilter)
|
||||
{
|
||||
query = query.Where(x => x.TenantId == tenantId);
|
||||
}
|
||||
|
||||
if (!includeDeleted)
|
||||
{
|
||||
query = query.Where(x => x.DeletedAt == null);
|
||||
}
|
||||
|
||||
// 2. 返回列表
|
||||
return query.ToListAsync(cancellationToken)
|
||||
.ContinueWith(t => (IReadOnlyList<IdentityUser>)t.Result, cancellationToken);
|
||||
// 4. (空行后) 返回列表
|
||||
return await query.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user