using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.Identity.Entities; using TakeoutSaaS.Domain.Identity.Repositories; namespace TakeoutSaaS.Infrastructure.Identity.Persistence; /// /// 角色模板仓储实现。 /// public sealed class EfRoleTemplateRepository(IdentityDbContext dbContext) : IRoleTemplateRepository { public Task> GetAllAsync(bool? isActive, CancellationToken cancellationToken = default) { var query = dbContext.RoleTemplates.AsNoTracking(); if (isActive.HasValue) { query = query.Where(x => x.IsActive == isActive.Value); } return query .OrderBy(x => x.TemplateCode) .ToListAsync(cancellationToken) .ContinueWith(t => (IReadOnlyList)t.Result, cancellationToken); } public Task FindByCodeAsync(string templateCode, CancellationToken cancellationToken = default) { var normalized = templateCode.Trim(); return dbContext.RoleTemplates.AsNoTracking().FirstOrDefaultAsync(x => x.TemplateCode == normalized, cancellationToken); } public Task> GetPermissionsAsync(long roleTemplateId, CancellationToken cancellationToken = default) { return dbContext.RoleTemplatePermissions.AsNoTracking() .Where(x => x.RoleTemplateId == roleTemplateId) .ToListAsync(cancellationToken) .ContinueWith(t => (IReadOnlyList)t.Result, cancellationToken); } public async Task>> GetPermissionsAsync(IEnumerable roleTemplateIds, CancellationToken cancellationToken = default) { var ids = roleTemplateIds.Distinct().ToArray(); if (ids.Length == 0) { return new Dictionary>(); } var permissions = await dbContext.RoleTemplatePermissions.AsNoTracking() .Where(x => ids.Contains(x.RoleTemplateId)) .ToListAsync(cancellationToken); return permissions .GroupBy(x => x.RoleTemplateId) .ToDictionary(g => g.Key, g => (IReadOnlyList)g.ToList()); } public async Task AddAsync(RoleTemplate template, IEnumerable permissionCodes, CancellationToken cancellationToken = default) { template.TemplateCode = template.TemplateCode.Trim(); template.Name = template.Name.Trim(); await dbContext.RoleTemplates.AddAsync(template, cancellationToken); await ReplacePermissionsInternalAsync(template, permissionCodes, cancellationToken); } public async Task UpdateAsync(RoleTemplate template, IEnumerable permissionCodes, CancellationToken cancellationToken = default) { template.TemplateCode = template.TemplateCode.Trim(); template.Name = template.Name.Trim(); dbContext.RoleTemplates.Update(template); await ReplacePermissionsInternalAsync(template, permissionCodes, cancellationToken); } public async Task DeleteAsync(long roleTemplateId, CancellationToken cancellationToken = default) { var entity = await dbContext.RoleTemplates.FirstOrDefaultAsync(x => x.Id == roleTemplateId, cancellationToken); if (entity != null) { var permissions = dbContext.RoleTemplatePermissions.Where(x => x.RoleTemplateId == roleTemplateId); dbContext.RoleTemplatePermissions.RemoveRange(permissions); dbContext.RoleTemplates.Remove(entity); } } public Task SaveChangesAsync(CancellationToken cancellationToken = default) => dbContext.SaveChangesAsync(cancellationToken); private async Task ReplacePermissionsInternalAsync(RoleTemplate template, IEnumerable permissionCodes, CancellationToken cancellationToken) { var strategy = dbContext.Database.CreateExecutionStrategy(); await strategy.ExecuteAsync(async () => { await using var trx = await dbContext.Database.BeginTransactionAsync(cancellationToken); // 1. 确保模板已持久化,便于 FK 正确填充 if (!dbContext.Entry(template).IsKeySet || template.Id == 0) { await dbContext.SaveChangesAsync(cancellationToken); } // 2. 归一化权限编码 var normalized = permissionCodes .Where(code => !string.IsNullOrWhiteSpace(code)) .Select(code => code.Trim()) .Distinct(StringComparer.OrdinalIgnoreCase) .ToArray(); // 3. 清空旧权限 var existing = await dbContext.RoleTemplatePermissions .Where(x => x.RoleTemplateId == template.Id) .ToListAsync(cancellationToken); dbContext.RoleTemplatePermissions.RemoveRange(existing); await dbContext.SaveChangesAsync(cancellationToken); // 4. 插入新权限 var toAdd = normalized.Select(code => new RoleTemplatePermission { RoleTemplateId = template.Id, PermissionCode = code }); await dbContext.RoleTemplatePermissions.AddRangeAsync(toAdd, cancellationToken); await dbContext.SaveChangesAsync(cancellationToken); await trx.CommitAsync(cancellationToken); }); } }