using Microsoft.EntityFrameworkCore;
using TakeoutSaaS.Domain.Identity.Entities;
using TakeoutSaaS.Domain.Identity.Repositories;
namespace TakeoutSaaS.Infrastructure.Identity.Persistence;
///
/// EF 用户-角色仓储。
///
public sealed class EfUserRoleRepository(IdentityDbContext dbContext) : IUserRoleRepository
{
///
/// 根据用户 ID 集合获取用户角色映射。
///
/// 租户 ID。
/// 用户 ID 集合。
/// 取消标记。
/// 用户角色映射列表。
public Task> GetByUserIdsAsync(long tenantId, IEnumerable userIds, CancellationToken cancellationToken = default)
=> dbContext.UserRoles.AsNoTracking()
.Where(x => x.TenantId == tenantId && userIds.Contains(x.UserId))
.ToListAsync(cancellationToken)
.ContinueWith(t => (IReadOnlyList)t.Result, cancellationToken);
///
/// 获取指定用户的角色集合。
///
/// 租户 ID。
/// 用户 ID。
/// 取消标记。
/// 用户角色列表。
public Task> GetByUserIdAsync(long tenantId, long userId, CancellationToken cancellationToken = default)
=> dbContext.UserRoles.AsNoTracking()
.Where(x => x.TenantId == tenantId && x.UserId == userId)
.ToListAsync(cancellationToken)
.ContinueWith(t => (IReadOnlyList)t.Result, cancellationToken);
///
/// 替换指定用户的角色集合。
///
/// 租户 ID。
/// 用户 ID。
/// 角色 ID 集合。
/// 取消标记。
/// 异步任务。
public async Task ReplaceUserRolesAsync(long tenantId, long userId, IEnumerable roleIds, CancellationToken cancellationToken = default)
{
// 1. 使用执行策略保障一致性
var strategy = dbContext.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(async () =>
{
await using var trx = await dbContext.Database.BeginTransactionAsync(cancellationToken);
// 2. 读取当前角色映射
var existing = await dbContext.UserRoles
.Where(x => x.TenantId == tenantId && x.UserId == userId)
.ToListAsync(cancellationToken);
// 3. 清空并保存
dbContext.UserRoles.RemoveRange(existing);
await dbContext.SaveChangesAsync(cancellationToken);
// 4. 构建新映射
var toAdd = roleIds.Distinct().Select(roleId => new UserRole
{
TenantId = tenantId,
UserId = userId,
RoleId = roleId
});
// 5. 批量新增并保存
await dbContext.UserRoles.AddRangeAsync(toAdd, cancellationToken);
await dbContext.SaveChangesAsync(cancellationToken);
await trx.CommitAsync(cancellationToken);
});
}
///
/// 保存仓储变更。
///
/// 取消标记。
/// 保存任务。
public Task SaveChangesAsync(CancellationToken cancellationToken = default)
=> dbContext.SaveChangesAsync(cancellationToken);
}