using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Tiku.Application.PlatformAdmin; using Tiku.Application.Security; using Tiku.Domain.Identity; using Tiku.Domain.Operations; using Tiku.Domain.Tenancy; namespace Tiku.Infrastructure.PlatformAdmin; internal sealed class PlatformStaffAccessService(PlatformAdministrationDependencies dependencies) : PlatformAdministrationServiceBase(dependencies), IPlatformStaffAccessService { public async Task GetStaffAsync( PlatformAdminActor actor, PlatformAdminQuery query, CancellationToken cancellationToken = default) { await AssertPlatformPermissionAsync(actor, BackendPermissions.PlatformStaffManage, cancellationToken); return await ExecuteSystemAsync("platform staff list", async dbContext => { var roleRows = await ( from userRole in dbContext.PlatformBackendUserRoles.AsNoTracking() join role in dbContext.PlatformBackendRoles.AsNoTracking() on userRole.RoleId equals role.Id join user in dbContext.Users.AsNoTracking() on userRole.UserId equals user.Id select new { user, role.Code }) .ToArrayAsync(cancellationToken); var items = roleRows .GroupBy(row => row.user.Id) .Select(group => ToStaffItem(group.First().user, group.Select(row => row.Code).Distinct(StringComparer.Ordinal).Order(StringComparer.Ordinal) .ToArray())) .OrderBy(item => item.Email ?? item.PhoneMasked ?? item.UserId.ToString()) .Take(Limit(query.Limit)) .ToArray(); return new PlatformStaffList(items); }, cancellationToken); } public async Task UpsertStaffAsync( PlatformAdminActor actor, UpsertPlatformStaffCommand command, CancellationToken cancellationToken = default) { await AssertPlatformPermissionAsync(actor, BackendPermissions.PlatformStaffManage, cancellationToken); return await ExecuteSystemAsync("platform staff upsert", async (provider, dbContext) => { var user = command.UserId.HasValue ? await dbContext.Users.SingleOrDefaultAsync(item => item.Id == command.UserId.Value, cancellationToken) : await dbContext.Users.SingleOrDefaultAsync(item => (!string.IsNullOrWhiteSpace(command.Email) && item.Email == command.Email) || (!string.IsNullOrWhiteSpace(command.Phone) && item.Phone == command.Phone), cancellationToken); if (user is null) { user = new User { Email = Normalize(command.Email), NormalizedEmail = Normalize(command.Email)?.ToUpperInvariant(), UserName = Normalize(command.Email) ?? Normalize(command.Phone), NormalizedUserName = (Normalize(command.Email) ?? Normalize(command.Phone))?.ToUpperInvariant(), Phone = Normalize(command.Phone), PhoneNumber = Normalize(command.Phone), Name = Normalize(command.Name), PrimaryRole = "platform_admin", Status = command.Status, ForcePasswordChange = true }; dbContext.Users.Add(user); } else { user.Email = Normalize(command.Email) ?? user.Email; user.NormalizedEmail = user.Email?.ToUpperInvariant(); user.Phone = Normalize(command.Phone) ?? user.Phone; user.PhoneNumber = user.Phone; user.Name = Normalize(command.Name) ?? user.Name; user.Status = command.Status; user.PrimaryRole = "platform_admin"; } var roleIds = command.RoleIds.Distinct().ToArray(); var roleCount = await dbContext.PlatformBackendRoles.CountAsync(role => roleIds.Contains(role.Id), cancellationToken); if (roleCount != roleIds.Length) throw new PlatformAdminException("One or more platform roles were not found.", "role_not_found"); await dbContext.SaveChangesAsync(cancellationToken); await dbContext.PlatformBackendUserRoles.Where(binding => binding.UserId == user.Id) .ExecuteDeleteAsync(cancellationToken); dbContext.PlatformBackendUserRoles.AddRange(roleIds.Select(roleId => new PlatformBackendUserRole { UserId = user.Id, RoleId = roleId })); AddAudit(dbContext, actor, "platform.staff.upserted", user.Id, new { user.Email, Phone = MaskPhone(user.Phone), user.Status, RoleIds = roleIds }); await dbContext.SaveChangesAsync(cancellationToken); var invalidator = provider.GetRequiredService(); await invalidator.InvalidateUserAsync(user.Id, cancellationToken); await invalidator.BumpScopeAsync(AuthRealm.Platform, null, cancellationToken); var roleCodes = await dbContext.PlatformBackendRoles.AsNoTracking() .Where(role => roleIds.Contains(role.Id)) .Select(role => role.Code) .ToArrayAsync(cancellationToken); return ToStaffItem(user, roleCodes); }, cancellationToken); } public async Task UpdateStaffStatusAsync( PlatformAdminActor actor, UpdatePlatformStaffStatusCommand command, CancellationToken cancellationToken = default) { await AssertPlatformPermissionAsync(actor, BackendPermissions.PlatformStaffManage, cancellationToken); return await ExecuteSystemAsync("platform staff status update", async (provider, dbContext) => { var user = await dbContext.Users.SingleOrDefaultAsync(item => item.Id == command.UserId, cancellationToken) ?? throw new PlatformAdminException("Platform staff user was not found.", "staff_not_found"); var from = user.Status; user.Status = command.Status; AddAudit(dbContext, actor, "platform.staff.status_changed", user.Id, new { From = from, To = command.Status, command.Reason }); await dbContext.SaveChangesAsync(cancellationToken); await provider.GetRequiredService() .InvalidateUserAsync(user.Id, cancellationToken); var roleCodes = await ( from binding in dbContext.PlatformBackendUserRoles.AsNoTracking() join role in dbContext.PlatformBackendRoles.AsNoTracking() on binding.RoleId equals role.Id where binding.UserId == user.Id select role.Code) .ToArrayAsync(cancellationToken); return ToStaffItem(user, roleCodes); }, cancellationToken); } }