DD.WellWorkover.Cloud/AsbCloudInfrastructure/Repository/UserRepository.cs

284 lines
10 KiB
C#
Raw Normal View History

using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudDb.Model;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using AsbCloudApp.Data.User;
2024-08-19 10:01:07 +05:00
namespace AsbCloudInfrastructure.Repository;
2024-08-19 10:01:07 +05:00
public class UserRepository : IUserRepository
{
private readonly IAsbCloudDbContext dbContext;
private readonly IUserRoleRepository userRoleRepository;
private const string userCacheTag = "User";
private const string relationUserUserRoleCacheTag = "RelationUserUserRole";
private static readonly TimeSpan cacheObsolence = TimeSpan.FromMinutes(15);
private static readonly TypeAdapterConfig userTypeAdapterConfig = TypeAdapterConfig<UserExtendedDto, User>
.NewConfig()
.Ignore(dst => dst.Company,
dst => dst.FileMarks,
dst => dst.Files,
dst => dst.RelationUsersUserRoles)
.Config;
private readonly IMemoryCache memoryCache;
public UserRepository(IAsbCloudDbContext dbContext, IUserRoleRepository userRoleRepository, IMemoryCache memoryCache)
{
this.dbContext = dbContext;
this.userRoleRepository = userRoleRepository;
this.memoryCache = memoryCache;
}
2022-12-14 10:38:44 +05:00
2024-08-19 10:01:07 +05:00
public async Task<int> InsertAsync(UserExtendedDto dto, CancellationToken token)
{
dto.Id = default;
var entity = Convert(dto);
await AssertLoginIsBusyAsync(dto.Login, token);
var userRoles = await userRoleRepository.GetByNamesAsync(dto.RoleNames, token).ConfigureAwait(false);
var updatedEntity = await dbContext.Users.AddAsync(entity, token).ConfigureAwait(false);
await dbContext.SaveChangesAsync(token);
if (userRoles?.Any() == true)
await UpdateRolesCacheForUserAsync(updatedEntity.Entity.Id, userRoles, token);
DropCacheUsers();
return updatedEntity.Entity.Id;
}
2024-08-19 10:01:07 +05:00
public Task<int> InsertRangeAsync(IEnumerable<UserExtendedDto> newItems, CancellationToken token)
{
throw new NotImplementedException();
}
2024-08-19 10:01:07 +05:00
public async Task<IEnumerable<UserExtendedDto>> GetAllAsync(CancellationToken token)
{
var users = await GetCacheUserAsync(token);
if (users is null)
return Enumerable.Empty<UserExtendedDto>();
var dtos = new List<UserExtendedDto>();
foreach(var user in users)
{
2024-08-19 10:01:07 +05:00
var dto = Convert(user);
dto.RoleNames = GetRolesNamesByIdUser(user.Id);
dtos.Add(dto);
};
return dtos;
}
2024-08-19 10:01:07 +05:00
public UserExtendedDto? GetOrDefault(int id)
{
var user = GetCacheUser().FirstOrDefault(u => u.Id == id);
if (user is null)
return null;
var dto = Convert(user);
dto.RoleNames = GetRolesNamesByIdUser(dto.Id);
return dto;
}
2024-08-19 10:01:07 +05:00
public async Task<UserExtendedDto?> GetOrDefaultAsync(int id, CancellationToken token)
{
var user = (await GetCacheUserAsync(token)).FirstOrDefault(u => u.Id == id);
if (user is null)
return null;
var dto = Convert(user);
dto.RoleNames = GetRolesNamesByIdUser(dto.Id);
return dto;
}
2024-08-19 10:01:07 +05:00
public async Task<int> UpdateAsync(UserExtendedDto dto, CancellationToken token)
{
if (dto.Id <= 1)
throw new ArgumentInvalidException(nameof(dto), $"Invalid id {dto.Id}. You can't edit this user.");
2024-08-19 10:01:07 +05:00
var oldUser = (await GetCacheUserAsync(token)).FirstOrDefault(u => u.Id == dto.Id)
?? throw new ArgumentInvalidException(nameof(dto), $"Invalid id {dto.Id}. You can't edit this user.");
2024-08-19 10:01:07 +05:00
if (oldUser.Login != dto.Login)
await AssertLoginIsBusyAsync(dto.Login, token);
2024-08-19 10:01:07 +05:00
var userRoles = await userRoleRepository.GetByNamesAsync(dto.RoleNames, token).ConfigureAwait(false);
await UpdateRolesCacheForUserAsync(dto.Id, userRoles, token);
2024-08-19 10:01:07 +05:00
var entity = dbContext.Users.FirstOrDefault(u => u.Id == dto.Id)
?? throw new ArgumentInvalidException(nameof(dto), $"Invalid id {dto.Id}. You can't edit this user.");
entity.Id = dto.Id;
entity.Login = dto.Login;
entity.Name = dto.Name;
entity.Email = dto.Email;
entity.Phone = dto.Phone;
entity.Surname = dto.Surname;
entity.Patronymic = dto.Patronymic;
entity.Position = dto.Position;
entity.IdCompany = dto.IdCompany;
entity.IdState = dto.IdState;
await dbContext.SaveChangesAsync(token);
DropCacheUsers();
return entity.Id;
}
2023-02-06 21:33:53 +05:00
2024-08-19 10:01:07 +05:00
public async Task<int> DeleteAsync(int id, CancellationToken token)
{
var user = (await GetCacheUserAsync(token)).FirstOrDefault(u => u.Id == id)
?? throw new ArgumentInvalidException(nameof(id), $"Invalid id {id}. You can't edit this user.");
var query = dbContext
.Users
.Where(u => u.Id == id);
if (query.Any())
{
var result = dbContext.Users.Remove(query.First());
await dbContext.SaveChangesAsync(token);
DropCacheUsers();
2024-08-19 10:01:07 +05:00
return result.Entity.Id;
}
2024-08-19 10:01:07 +05:00
throw new ArgumentInvalidException
(nameof(id), $"Invalid id {id}. You can't edit this user.");
}
2024-08-19 10:01:07 +05:00
public IEnumerable<UserRoleDto> GetRolesByIdUser(int idUser, int nestedLevel = 0)
{
var roles = GetCachRelationUserUserRoleCacheTag().Where(r => r.IdUser == idUser);
return roles.SelectMany(r => userRoleRepository.GetNestedById(r.IdUserRole, nestedLevel));
}
2024-08-19 10:01:07 +05:00
public IEnumerable<PermissionDto> GetNestedPermissions(int idUser)
{
var roles = GetRolesByIdUser(idUser, 7);
if (roles is null)
return Enumerable.Empty<PermissionDto>();
2023-09-29 12:06:46 +05:00
2024-08-19 10:01:07 +05:00
var permissions = roles
.Where(r => r.Permissions is not null)
.SelectMany(r => r.Permissions);
2024-08-19 10:01:07 +05:00
return permissions;
}
2024-08-19 10:01:07 +05:00
public bool HasPermission(int idUser, string permissionName)
{
if (idUser == 1)
return true;
2024-08-19 10:01:07 +05:00
var relationsToRoles = GetCachRelationUserUserRoleCacheTag()
.Where(r => r.IdUser == idUser);
if (relationsToRoles is null)
return false;
2024-08-19 10:01:07 +05:00
return userRoleRepository.HasPermission(relationsToRoles
.Select(r => r.IdUserRole), permissionName);
}
2024-08-19 10:01:07 +05:00
private IEnumerable<string> GetRolesNamesByIdUser(int idUser)
{
var userRoles = GetRolesByIdUser(idUser, 7)
.Select(r => r.Caption)
.Distinct();
if (userRoles.Any())
return userRoles;
return Enumerable.Empty<string>();
}
2024-08-19 10:01:07 +05:00
private async Task AssertLoginIsBusyAsync(string login, CancellationToken token)
{
var existingUserDto = (await GetCacheUserAsync(token))
.FirstOrDefault(u => u.Login.ToLower() == login.ToLower());
2022-10-27 15:49:22 +05:00
2024-08-19 10:01:07 +05:00
if (existingUserDto is not null)
throw new ArgumentInvalidException(nameof(login), $"Login {login} is busy by {existingUserDto.MakeDisplayName()}, id{existingUserDto.Id}");
}
private IEnumerable<RelationUserUserRole> GetCachRelationUserUserRoleCacheTag()
{
var cache = memoryCache.GetOrCreate(relationUserUserRoleCacheTag, cacheEntry =>
{
2024-08-19 10:01:07 +05:00
cacheEntry.AbsoluteExpirationRelativeToNow = cacheObsolence;
cacheEntry.SlidingExpiration = cacheObsolence;
var query = dbContext.RelationUserUserRoles
.Include(r => r.UserRole)
.Include(r => r.User);
var entities = query.ToArray();
return entities;
});
return cache!;
}
2023-09-29 12:06:46 +05:00
2024-08-19 10:01:07 +05:00
private void DropCacheRelationUserUserRoleCacheTag()
{
memoryCache.Remove(relationUserUserRoleCacheTag);
}
2024-08-19 10:01:07 +05:00
private async Task UpdateRolesCacheForUserAsync(int idUser, IEnumerable<UserRoleDto> roleDtos, CancellationToken token)
{
var relations = dbContext.RelationUserUserRoles.Where(r => r.IdUser == idUser);
dbContext.RelationUserUserRoles.RemoveRange(relations);
var entityRoles = roleDtos.Select(role => new RelationUserUserRole
{
2024-08-19 10:01:07 +05:00
IdUser = idUser,
IdUserRole = role.Id
});
dbContext.RelationUserUserRoles.AddRange(entityRoles);
await dbContext.SaveChangesAsync(token);
DropCacheRelationUserUserRoleCacheTag();
}
2024-08-19 10:01:07 +05:00
private void DropCacheUsers()
=> memoryCache.Remove(userCacheTag);
2023-02-13 15:39:17 +05:00
2024-08-19 10:01:07 +05:00
private IEnumerable<User> GetCacheUser()
{
var cache = memoryCache.GetOrCreate(userCacheTag, cacheEntry =>
{
2024-08-19 10:01:07 +05:00
cacheEntry.AbsoluteExpirationRelativeToNow = cacheObsolence;
cacheEntry.SlidingExpiration = cacheObsolence;
var query = dbContext.Users
.Include(r => r.Company)
.Include(r => r.RelationUsersUserRoles);
var entities = query.ToArray();
return entities;
});
return cache!;
}
2024-08-19 10:01:07 +05:00
private Task<IEnumerable<User>> GetCacheUserAsync(CancellationToken token)
{
var cache = memoryCache.GetOrCreateAsync(userCacheTag, async (cacheEntry) =>
{
2024-08-19 10:01:07 +05:00
cacheEntry.AbsoluteExpirationRelativeToNow = cacheObsolence;
cacheEntry.SlidingExpiration = cacheObsolence;
var query = dbContext.Users
.Include(r => r.Company)
.Include(r => r.RelationUsersUserRoles);
var entities = await query.ToArrayAsync(token);
return entities.AsEnumerable();
});
return cache!;
}
2024-08-19 10:01:07 +05:00
protected User Convert(UserExtendedDto dto)
{
var entity = dto.Adapt<User>(userTypeAdapterConfig);
if (string.IsNullOrEmpty(entity.PasswordHash))
{
2024-08-19 10:01:07 +05:00
var hash = dbContext.Users.FirstOrDefault(u => u.Id == dto.Id)?.PasswordHash;
entity.PasswordHash = hash ?? string.Empty;
}
2024-08-19 10:01:07 +05:00
return entity;
}
2024-08-19 10:01:07 +05:00
protected virtual UserExtendedDto Convert(User entity)
{
var dto = entity.Adapt<UserExtendedDto>();
return dto;
}
}