diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IOrganizationUnitRepository.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IOrganizationUnitRepository.cs index a9a92e9e43..20f39bc55f 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IOrganizationUnitRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IOrganizationUnitRepository.cs @@ -98,18 +98,21 @@ public interface IOrganizationUnitRepository : IBasicRepository> GetMemberIdsAsync( Guid id, + bool includeChildren = false, CancellationToken cancellationToken = default ); Task GetMembersCountAsync( OrganizationUnit organizationUnit, string filter = null, + bool includeChildren = false, CancellationToken cancellationToken = default ); diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs index 74cb486f15..a4fc897bf4 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs @@ -420,14 +420,14 @@ public class IdentityUserManager : UserManager, IDomainService var sourceOrganization = await OrganizationUnitRepository.GetAsync(sourceOrganizationId, cancellationToken: CancellationToken); Logger.LogDebug($"Remove dynamic claims cache for users of organization: {sourceOrganizationId}"); - var userIdList = await OrganizationUnitRepository.GetMemberIdsAsync(sourceOrganizationId, cancellationToken: CancellationToken); + var userIdList = await OrganizationUnitRepository.GetMemberIdsAsync(sourceOrganizationId, includeChildren: true, cancellationToken: CancellationToken); await DynamicClaimCache.RemoveManyAsync(userIdList.Select(userId => AbpDynamicClaimCacheItem.CalculateCacheKey(userId, sourceOrganization.TenantId)), token: CancellationToken); var targetOrganization = targetOrganizationId.HasValue ? await OrganizationUnitRepository.GetAsync(targetOrganizationId.Value, cancellationToken: CancellationToken) : null; if (targetOrganization != null) { Logger.LogDebug($"Remove dynamic claims cache for users of organization: {targetOrganizationId}"); - userIdList = await OrganizationUnitRepository.GetMemberIdsAsync(targetOrganizationId.Value, cancellationToken: CancellationToken); + userIdList = await OrganizationUnitRepository.GetMemberIdsAsync(targetOrganizationId.Value, includeChildren: true, cancellationToken: CancellationToken); await DynamicClaimCache.RemoveManyAsync(userIdList.Select(userId => AbpDynamicClaimCacheItem.CalculateCacheKey(userId, targetOrganization.TenantId)), token: CancellationToken); } diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs index a623894625..25646ac90a 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; @@ -424,9 +425,20 @@ public class EfCoreIdentityUserRepository : EfCoreRepository().FirstOrDefaultAsync(x => x.Id == sourceOrganizationId, cancellationToken: cancellationToken); + if (sourceOrganization == null) + { + throw new EntityNotFoundException(typeof(OrganizationUnit), sourceOrganizationId); + } + + var allSourceOrganizationIds = await (await GetDbContextAsync()).Set() + .Where(x => x.Code.StartsWith(sourceOrganization.Code)) + .Select(x => x.Id).ToArrayAsync(cancellationToken: cancellationToken); + var users = await (await GetDbContextAsync()).Set().Where(x => x.OrganizationUnitId == targetOrganizationId).Select(x => x.UserId).ToArrayAsync(cancellationToken: cancellationToken); - await (await GetDbContextAsync()).Set().Where(x => x.OrganizationUnitId == sourceOrganizationId && !users.Contains(x.UserId)).ExecuteUpdateAsync(t => t.SetProperty(e => e.OrganizationUnitId, targetOrganizationId), GetCancellationToken(cancellationToken)); - await (await GetDbContextAsync()).Set().Where(x => x.OrganizationUnitId == sourceOrganizationId).ExecuteDeleteAsync(GetCancellationToken(cancellationToken)); + + await (await GetDbContextAsync()).Set().Where(x => allSourceOrganizationIds.Contains(x.OrganizationUnitId) && !users.Contains(x.UserId)).ExecuteUpdateAsync(t => t.SetProperty(e => e.OrganizationUnitId, targetOrganizationId), GetCancellationToken(cancellationToken)); + await (await GetDbContextAsync()).Set().Where(x => allSourceOrganizationIds.Contains(x.OrganizationUnitId)).ExecuteDeleteAsync(GetCancellationToken(cancellationToken)); } else { diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs index 03649b3c73..c0d664fbd4 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs @@ -208,32 +208,31 @@ public class EfCoreOrganizationUnitRepository int maxResultCount = int.MaxValue, int skipCount = 0, string filter = null, + bool includeChildren = false, bool includeDetails = false, CancellationToken cancellationToken = default) { - var query = await CreateGetMembersFilteredQueryAsync(organizationUnit, filter); + var query = await CreateGetMembersFilteredQueryAsync(organizationUnit, filter, includeChildren); return await query.IncludeDetails(includeDetails).OrderBy(sorting.IsNullOrEmpty() ? nameof(IdentityUser.UserName) : sorting) .PageBy(skipCount, maxResultCount) .ToListAsync(GetCancellationToken(cancellationToken)); } - public virtual async Task> GetMemberIdsAsync(Guid id, CancellationToken cancellationToken = default) + public virtual async Task> GetMemberIdsAsync(Guid id, bool includeChildren = false, CancellationToken cancellationToken = default) { - var dbContext = await GetDbContextAsync(); - - return await (from userOu in dbContext.Set() - join user in dbContext.Users on userOu.UserId equals user.Id - where userOu.OrganizationUnitId == id - select user.Id).ToListAsync(cancellationToken); + var organizationUnit = await GetAsync(id, cancellationToken: cancellationToken); + var query = await CreateGetMembersFilteredQueryAsync(organizationUnit, null, includeChildren); + return await query.Select(x => x.Id).ToListAsync(cancellationToken); } public virtual async Task GetMembersCountAsync( OrganizationUnit organizationUnit, string filter = null, + bool includeChildren = false, CancellationToken cancellationToken = default) { - var query = await CreateGetMembersFilteredQueryAsync(organizationUnit, filter); + var query = await CreateGetMembersFilteredQueryAsync(organizationUnit, filter, includeChildren); return await query.CountAsync(GetCancellationToken(cancellationToken)); } @@ -324,14 +323,33 @@ public class EfCoreOrganizationUnitRepository dbContext.Set().RemoveRange(ouMembersQuery); } - protected virtual async Task> CreateGetMembersFilteredQueryAsync(OrganizationUnit organizationUnit, string filter = null) + protected virtual async Task> CreateGetMembersFilteredQueryAsync( + OrganizationUnit organizationUnit, + string filter = null, + bool includeChildren = false) { var dbContext = await GetDbContextAsync(); - var query = from userOu in dbContext.Set() - join user in dbContext.Users on userOu.UserId equals user.Id - where userOu.OrganizationUnitId == organizationUnit.Id - select user; + IQueryable query; + if (includeChildren) + { + var childrenIds = await (await GetDbSetAsync()) + .Where(ou => ou.Code.StartsWith(organizationUnit.Code)) + .Select(x => x.Id) + .ToListAsync(); + + query = from userOu in dbContext.Set() + join user in dbContext.Users on userOu.UserId equals user.Id + where childrenIds.Contains(userOu.OrganizationUnitId) + select user; + } + else + { + query = from userOu in dbContext.Set() + join user in dbContext.Users on userOu.UserId equals user.Id + where userOu.OrganizationUnitId == organizationUnit.Id + select user; + } if (!filter.IsNullOrWhiteSpace()) { diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs index 64c47f8d2c..0a84a429f0 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using JetBrains.Annotations; using MongoDB.Driver; using MongoDB.Driver.Linq; +using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Repositories.MongoDB; using Volo.Abp.MongoDB; @@ -367,7 +368,11 @@ public class MongoIdentityUserRepository : MongoDbRepository> GetMemberIdsAsync(Guid id, CancellationToken cancellationToken = default) + public virtual async Task> GetMemberIdsAsync(Guid id, bool includeChildren = false, CancellationToken cancellationToken = default) { cancellationToken = GetCancellationToken(cancellationToken); return await (await GetQueryableAsync(cancellationToken)) @@ -206,10 +207,11 @@ public class MongoOrganizationUnitRepository public virtual async Task GetMembersCountAsync( OrganizationUnit organizationUnit, string filter = null, + bool includeChildren = false, CancellationToken cancellationToken = default) { cancellationToken = GetCancellationToken(cancellationToken); - var query = await CreateGetMembersFilteredQueryAsync(organizationUnit, filter, cancellationToken); + var query = await CreateGetMembersFilteredQueryAsync(organizationUnit, filter, includeChildren, cancellationToken); return await query.CountAsync(cancellationToken); } @@ -277,6 +279,7 @@ public class MongoOrganizationUnitRepository protected virtual async Task> CreateGetMembersFilteredQueryAsync( OrganizationUnit organizationUnit, string filter = null, + bool includeChildren = false, CancellationToken cancellationToken = default) { return (await GetQueryableAsync(cancellationToken)) diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs index 8746c3a1fd..ce78147ace 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs @@ -267,6 +267,10 @@ public abstract class OrganizationUnitRepository_Tests : AbpIden var usersCount = await _organizationUnitRepository.GetMembersCountAsync(ou); usersCount.ShouldBeGreaterThan(1); + + usersCount = await _organizationUnitRepository.GetMembersCountAsync(ou, includeChildren: true); + + usersCount.ShouldBeGreaterThanOrEqualTo(2); } [Fact] @@ -301,6 +305,9 @@ public abstract class OrganizationUnitRepository_Tests : AbpIden await _organizationUnitRepository.RemoveAllMembersAsync(ou); var newCount = await _organizationUnitRepository.GetMembersCountAsync(ou); newCount.ShouldBe(0); + + newCount = await _organizationUnitRepository.GetMembersCountAsync(ou, includeChildren: true); + newCount.ShouldBe(0); } [Fact]