diff --git a/AsbCloudApp/Data/Subsystems/SubsystemActiveWellStatDto.cs b/AsbCloudApp/Data/Subsystems/SubsystemActiveWellStatDto.cs new file mode 100644 index 00000000..2b62f171 --- /dev/null +++ b/AsbCloudApp/Data/Subsystems/SubsystemActiveWellStatDto.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +namespace AsbCloudApp.Data.Subsystems +{ +#nullable enable + /// + /// Статистика наработки подсистем по активным скважинам + /// + public class SubsystemActiveWellStatDto + { + /// + /// Активная скважина + /// + public WellDto Well { get; set; } + /// + /// Наработки подсистемы АКБ + /// + public SubsystemStatDto? SubsystemAKB { get; set; } + /// + /// Наработки подсистемы МСЕ + /// + public SubsystemStatDto? SubsystemMSE { get; set; } + /// + /// Наработки подсистемы СПИН + /// + public SubsystemStatDto? SubsystemSpinMaster { get; set; } + /// + /// Наработки подсистемы ТОРК + /// + public SubsystemStatDto? SubsystemTorqueMaster { get; set; } + } +#nullable disable +} \ No newline at end of file diff --git a/AsbCloudApp/Data/UserDto.cs b/AsbCloudApp/Data/UserDto.cs index 15cc5408..ed065f60 100644 --- a/AsbCloudApp/Data/UserDto.cs +++ b/AsbCloudApp/Data/UserDto.cs @@ -58,5 +58,25 @@ /// DTO компании /// public CompanyDto Company { get; set; } + + /// + /// Получение отображаемого имени + /// + /// + public string MakeDisplayName() + { + if (string.IsNullOrEmpty(Surname)) + return Login; + + var s = Surname; + if (!string.IsNullOrEmpty(Name)) + { + s += $"{Name[0]}."; + if (!string.IsNullOrEmpty(Patronymic)) + s += $" {Patronymic[0]}."; + } + + return s; + } } } diff --git a/AsbCloudApp/Services/IUserService.cs b/AsbCloudApp/Repositories/IUserRepository.cs similarity index 81% rename from AsbCloudApp/Services/IUserService.cs rename to AsbCloudApp/Repositories/IUserRepository.cs index 34f12212..4f6b0fac 100644 --- a/AsbCloudApp/Services/IUserService.cs +++ b/AsbCloudApp/Repositories/IUserRepository.cs @@ -1,18 +1,14 @@ using AsbCloudApp.Data; +using AsbCloudApp.Services; using System.Collections.Generic; -namespace AsbCloudApp.Services +namespace AsbCloudApp.Repositories { /// - /// Сервис пользователей + /// Репозиторий пользователей /// - public interface IUserService : ICrudService + public interface IUserRepository : ICrudService { - /// - /// Сервис ролей - /// - IUserRoleService RoleService { get; } - /// /// Получить список всех прав пользователя (включая наследование групп) /// diff --git a/AsbCloudApp/Services/IUserRoleService.cs b/AsbCloudApp/Repositories/IUserRoleRepository.cs similarity index 83% rename from AsbCloudApp/Services/IUserRoleService.cs rename to AsbCloudApp/Repositories/IUserRoleRepository.cs index b9341c9d..a2c25704 100644 --- a/AsbCloudApp/Services/IUserRoleService.cs +++ b/AsbCloudApp/Repositories/IUserRoleRepository.cs @@ -1,16 +1,17 @@ using AsbCloudApp.Data; +using AsbCloudApp.Services; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; +using System.Threading; -namespace AsbCloudApp.Services +namespace AsbCloudApp.Repositories { +#nullable enable /// - /// Репозиторий ролей пользователя + /// Разрешения на доступ к данным /// - public interface IUserRoleService : ICrudService + public interface IUserRoleRepository : ICrudService { - // todo: переименовать /// /// получить dto по названиям /// @@ -35,4 +36,5 @@ namespace AsbCloudApp.Services /// bool HasPermission(IEnumerable rolesIds, string permissionName); } -} \ No newline at end of file +#nullable disable +} diff --git a/AsbCloudApp/Services/IDetectedOperationService.cs b/AsbCloudApp/Services/IDetectedOperationService.cs index 14a05665..8418557c 100644 --- a/AsbCloudApp/Services/IDetectedOperationService.cs +++ b/AsbCloudApp/Services/IDetectedOperationService.cs @@ -1,6 +1,7 @@ using AsbCloudApp.Data; using AsbCloudApp.Data.DetectedOperation; using AsbCloudApp.Requests; +using System; using System.Collections.Generic; using System.IO; using System.Threading; @@ -39,6 +40,16 @@ namespace AsbCloudApp.Services /// Task?> GetOperationsAsync(DetectedOperationRequest request, CancellationToken token); + /// + /// Получить интервалы глубин по всем скважинам + /// + /// список ИД телеметрий активных скважин + /// + /// + /// + /// кортеж - ид телеметрии, интервалы глубины забоя (ротор,слайд) + Task> GetDepthIntervalAllOperationsAsync(IEnumerable telemetryIds,DateTimeOffset gtDate, DateTimeOffset ltDate, CancellationToken token); + /// /// Удалить операции /// diff --git a/AsbCloudApp/Services/IWellFinalDocumentsService.cs b/AsbCloudApp/Services/IWellFinalDocumentsService.cs index 7b910c74..4da098ce 100644 --- a/AsbCloudApp/Services/IWellFinalDocumentsService.cs +++ b/AsbCloudApp/Services/IWellFinalDocumentsService.cs @@ -50,7 +50,8 @@ namespace AsbCloudApp.Services /// /// Сохранение файла /// - /// + /// + /// /// /// /// diff --git a/AsbCloudApp/Services/Subsystems/ISubsystemOperationTimeService.cs b/AsbCloudApp/Services/Subsystems/ISubsystemOperationTimeService.cs index 62ade194..91b78023 100644 --- a/AsbCloudApp/Services/Subsystems/ISubsystemOperationTimeService.cs +++ b/AsbCloudApp/Services/Subsystems/ISubsystemOperationTimeService.cs @@ -46,7 +46,16 @@ namespace AsbCloudApp.Services.Subsystems /// /// /// - Task GetDateRangeOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token); + Task GetDateRangeOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token); + /// + /// Получение статистики по наработке подсистем по активным скважинам + /// + /// + /// + /// + /// + /// + Task> GetStatByActiveWells(int idCompany, DateTime? gtDate, DateTime? ltDate, CancellationToken token); } #nullable disable } diff --git a/AsbCloudDb/Model/IAsbCloudDbContext.cs b/AsbCloudDb/Model/IAsbCloudDbContext.cs index 42642377..5bab3c21 100644 --- a/AsbCloudDb/Model/IAsbCloudDbContext.cs +++ b/AsbCloudDb/Model/IAsbCloudDbContext.cs @@ -28,6 +28,7 @@ namespace AsbCloudDb.Model DbSet RelationDrillingProgramPartUsers { get; } DbSet RelationUserRolePermissions { get; } DbSet RelationUserUserRoles { get; } + DbSet RelationUserRoleUserRoles { get; } DbSet ReportProperties { get; } DbSet Subsystems { get; } DbSet SubsystemOperationTimes { get; } diff --git a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj index b38c4f87..622173cc 100644 --- a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj +++ b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj @@ -9,6 +9,8 @@ + + @@ -16,7 +18,10 @@ - + + PreserveNewest + + PreserveNewest @@ -43,7 +48,6 @@ - diff --git a/AsbCloudInfrastructure/CommonLibs/AsbSaubReport.dll b/AsbCloudInfrastructure/CommonLibs/AsbSaubReport.dll index b7ad78ab..d4617aeb 100644 Binary files a/AsbCloudInfrastructure/CommonLibs/AsbSaubReport.dll and b/AsbCloudInfrastructure/CommonLibs/AsbSaubReport.dll differ diff --git a/AsbCloudInfrastructure/CommonLibs/AsbSaubReportPdf.dll b/AsbCloudInfrastructure/CommonLibs/AsbSaubReportPdf.dll index 5b578d9f..05fe81d6 100644 Binary files a/AsbCloudInfrastructure/CommonLibs/AsbSaubReportPdf.dll and b/AsbCloudInfrastructure/CommonLibs/AsbSaubReportPdf.dll differ diff --git a/AsbCloudInfrastructure/CommonLibs/Readme.md b/AsbCloudInfrastructure/CommonLibs/Readme.md new file mode 100644 index 00000000..64cb4b4c --- /dev/null +++ b/AsbCloudInfrastructure/CommonLibs/Readme.md @@ -0,0 +1,2 @@ +# Библиотека формирования рапортов в формате PDF. +Для брендирования положить рядом с библиотекой файл logo_720x404.png \ No newline at end of file diff --git a/AsbCloudInfrastructure/CommonLibs/logo_720x404.png b/AsbCloudInfrastructure/CommonLibs/logo_720x404.png new file mode 100644 index 00000000..90b02fb8 Binary files /dev/null and b/AsbCloudInfrastructure/CommonLibs/logo_720x404.png differ diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 48d759fc..a9e521ca 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -120,16 +120,12 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); - services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); - //services.AddTransient(); - services.AddTransient(); services.AddTransient(); services.AddTransient, CrudWellRelatedServiceBase>(); @@ -165,6 +161,8 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); // Subsystem service services.AddTransient, CrudCacheServiceBase>(); services.AddTransient(); diff --git a/AsbCloudInfrastructure/EfCache/EfCacheExtensions.cs b/AsbCloudInfrastructure/EfCache/EfCacheExtensions.cs index e9f17c32..0a93c70c 100644 --- a/AsbCloudInfrastructure/EfCache/EfCacheExtensions.cs +++ b/AsbCloudInfrastructure/EfCache/EfCacheExtensions.cs @@ -21,6 +21,82 @@ namespace AsbCloudInfrastructure.EfCache private static readonly TimeSpan minCacheTime = TimeSpan.FromSeconds(2); private static readonly TimeSpan defaultObsolescence = TimeSpan.FromMinutes(4); + private class YieldConvertedData : IEnumerable + { + private struct ConvertedData + { + public TEntity? Entity; + public TModel? Model; + } + + ConvertedData[] data; + public Func convert { get; } + + public YieldConvertedData(TEntity[] entities, Func convert) + { + data = (entities.Select(x => new ConvertedData { + Entity = x, + Model = default })) + .ToArray(); + this.convert = convert; + } + + class YieldConvertedDataEnumerator : IEnumerator + { + private readonly ConvertedData[] data; + private readonly Func convert; + private int position = -1; + + public YieldConvertedDataEnumerator(ConvertedData[] data, Func convert) + { + this.data = data; + this.convert = convert; + } + + public TModel Current + { + get + { + if (data[position].Entity is TEntity entity) + { + var dto = convert(entity); + data[position].Entity = default; + data[position].Model = dto; + } + return data[position].Model!; + } + } + + object IEnumerator.Current => Current!; + + public void Dispose() + { + } + + public bool MoveNext() + { + position++; + return (position < data.Length); + } + + public void Reset() + { + position = -1; + } + } + + public IEnumerator GetEnumerator() + { + var result = new YieldConvertedDataEnumerator(data, convert); + return result; + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } + private class CacheItem { internal IEnumerable? Data; @@ -45,14 +121,10 @@ namespace AsbCloudInfrastructure.EfCache { try { - var convertedData = typedEntityData.Select(convert).ToList(); + var convertedData = new YieldConvertedData(typedEntityData.ToArray(), convert); Data = convertedData; return convertedData; } - catch - { - throw; - } finally { semaphore.Release(); @@ -67,7 +139,7 @@ namespace AsbCloudInfrastructure.EfCache else { semaphore.Release(); - throw new TimeoutException("EfCacheL2.GetOrAddCache. Can't wait too long while converting cache data"); + throw new TimeoutException("EfCacheL2.GetData. Can't wait too long while converting cache data"); } } } @@ -77,7 +149,7 @@ namespace AsbCloudInfrastructure.EfCache } } - private static CacheItem GetOrAddCache(string tag, Func valueFactory, TimeSpan obsolete) + private static CacheItem GetOrAddCache(string tag, Func valueFactory, TimeSpan obsolete) { CacheItem cache; while (!caches.ContainsKey(tag)) @@ -86,12 +158,11 @@ namespace AsbCloudInfrastructure.EfCache { try { - cache = new CacheItem(); - caches.Add(tag, cache); - } - catch - { - throw; + if (!caches.ContainsKey(tag)) + { + cache = new CacheItem(); + caches.Add(tag, cache); + } } finally { @@ -133,10 +204,6 @@ namespace AsbCloudInfrastructure.EfCache cache.DateObsolete = dateObsolete; cache.DateObsoleteTotal = dateObsolete + queryTime + minCacheTime; } - catch - { - throw; - } finally { cache.semaphore.Release(); @@ -158,7 +225,7 @@ namespace AsbCloudInfrastructure.EfCache return cache; } - private static async Task GetOrAddCacheAsync(string tag, Func> valueFactoryAsync, TimeSpan obsolete, CancellationToken token) + private static async Task GetOrAddCacheAsync(string tag, Func> valueFactoryAsync, TimeSpan obsolete, CancellationToken token) { CacheItem cache; while (!caches.ContainsKey(tag)) @@ -167,12 +234,11 @@ namespace AsbCloudInfrastructure.EfCache { try { - cache = new CacheItem(); - caches.Add(tag, cache); - } - catch - { - throw; + if (!caches.ContainsKey(tag)) + { + cache = new CacheItem(); + caches.Add(tag, cache); + } } finally { @@ -214,10 +280,6 @@ namespace AsbCloudInfrastructure.EfCache cache.DateObsolete = dateObsolete; cache.DateObsoleteTotal = dateObsolete + queryTime + minCacheTime; } - catch - { - throw; - } finally { cache.semaphore.Release(); @@ -263,7 +325,7 @@ namespace AsbCloudInfrastructure.EfCache public static IEnumerable FromCache(this IQueryable query, string tag, TimeSpan obsolescence) where TEntity : class { - IEnumerable factory() => query.AsNoTracking().ToList(); + object[] factory() => query.AsNoTracking().ToArray(); var cache = GetOrAddCache(tag, factory, obsolescence); return cache.GetData(); } @@ -282,7 +344,7 @@ namespace AsbCloudInfrastructure.EfCache public static IEnumerable FromCache(this IQueryable query, string tag, TimeSpan obsolescence, Func convert) where TEntity : class { - IEnumerable factory() => query.AsNoTracking().ToList(); + object[] factory() => query.AsNoTracking().ToArray(); var cache = GetOrAddCache(tag, factory, obsolescence); return cache.GetData(convert); } @@ -306,8 +368,8 @@ namespace AsbCloudInfrastructure.EfCache public static async Task> FromCacheAsync(this IQueryable query, string tag, TimeSpan obsolescence, CancellationToken token = default) where TEntity : class { - async Task factory(CancellationToken token) - => await query.AsNoTracking().ToListAsync(token); + async Task factory(CancellationToken token) + => await query.AsNoTracking().ToArrayAsync(token); var cache = await GetOrAddCacheAsync(tag, factory, obsolescence, token); return cache.GetData(); } @@ -327,8 +389,8 @@ namespace AsbCloudInfrastructure.EfCache public static async Task> FromCacheAsync(this IQueryable query, string tag, TimeSpan obsolescence, Func convert, CancellationToken token = default) where TEntity : class { - async Task factory(CancellationToken token) - => await query.AsNoTracking().ToListAsync(token); + async Task factory(CancellationToken token) + => await query.AsNoTracking().ToArrayAsync(token); var cache = await GetOrAddCacheAsync(tag, factory, obsolescence, token); return cache.GetData(convert); } diff --git a/AsbCloudInfrastructure/Repository/UserRepository.cs b/AsbCloudInfrastructure/Repository/UserRepository.cs new file mode 100644 index 00000000..3a52d030 --- /dev/null +++ b/AsbCloudInfrastructure/Repository/UserRepository.cs @@ -0,0 +1,228 @@ +using AsbCloudApp.Data; +using AsbCloudApp.Exceptions; +using AsbCloudApp.Repositories; +using AsbCloudDb; +using AsbCloudDb.Model; +using AsbCloudInfrastructure.EfCache; +using Mapster; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudInfrastructure.Repository +{ +#nullable enable + 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 + .NewConfig() + .Ignore(dst => dst.Company, + dst => dst.FileMarks, + dst => dst.Files, + dst => dst.RelationUsersUserRoles) + .Config; + + public UserRepository(IAsbCloudDbContext dbContext, IUserRoleRepository userRoleRepository) { + this.dbContext = dbContext; + this.userRoleRepository = userRoleRepository; + } + + public async Task InsertAsync(UserExtendedDto dto, CancellationToken token = default) + { + 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); + if (userRoles?.Any() == true) + await UpdateRolesCacheForUserAsync(updatedEntity.Entity.Id, userRoles, token); + + await dbContext.SaveChangesAsync(token); + DropCacheUsers(); + return updatedEntity.Entity.Id; + } + + public Task InsertRangeAsync(IEnumerable newItems, CancellationToken token = default) + { + throw new NotImplementedException(); + } + + public async Task> GetAllAsync(CancellationToken token = default) + { + var dtos = (await GetCacheUserAsync(token)).ToList(); + if (dtos is null) + return Enumerable.Empty(); + + for (var i = 0; i < dtos.Count; i++) + dtos[i].RoleNames = GetRolesNamesByIdUser(dtos[i].Id); + return dtos; + } + + public UserExtendedDto? GetOrDefault(int id) + { + var dto = GetCacheUser().FirstOrDefault(u => u.Id == id); + if (dto is null) + return null; + var entity = Convert(dto); + dto.RoleNames = GetRolesNamesByIdUser(dto.Id); + return dto; + } + + public async Task GetOrDefaultAsync(int id, CancellationToken token = default) + { + var dto = (await GetCacheUserAsync(token)).FirstOrDefault(u => u.Id == id); + if (dto is null) + return null; + + dto.RoleNames = GetRolesNamesByIdUser(dto.Id); + return dto; + } + + public async Task UpdateAsync(UserExtendedDto dto, CancellationToken token = default) + { + if (dto.Id <= 1) + throw new ArgumentInvalidException($"Invalid id {dto.Id}. You can't edit this user.", nameof(dto)); + + var oldUser = (await GetCacheUserAsync(token)).FirstOrDefault(u => u.Id == dto.Id); + if (oldUser is null) + return 0; + + if (oldUser.Login != dto.Login) + await AssertLoginIsBusyAsync(dto.Login, token); + + var userRoles = await userRoleRepository.GetByNamesAsync(dto.RoleNames, token).ConfigureAwait(false); + await UpdateRolesCacheForUserAsync(dto.Id, userRoles, token); + + var entity = Convert(dto); + + var result = dbContext.Users.Upsert(entity); + await dbContext.SaveChangesAsync(token); + DropCacheUsers(); + return result.Entity.Id; + } + + public async Task DeleteAsync(int id, CancellationToken token = default) + { + var dto = (await GetCacheUserAsync(token)).FirstOrDefault(u => u.Id == id); + if (dto is null) + return 0; + + var entity = Convert(dto); + var result = dbContext.Users.Remove(entity); + await dbContext.SaveChangesAsync(token); + DropCacheUsers(); + return result.Entity.Id; + } + + public IEnumerable GetRolesByIdUser(int idUser, int nestedLevel = 0) + { + var roles = GetCachRelationUserUserRoleCacheTag().Where(r => r.IdUser == idUser); + return roles.SelectMany(r => userRoleRepository.GetNestedById(r.IdUserRole, nestedLevel)); + } + + public IEnumerable GetNestedPermissions(int idUser) + { + var roles = GetRolesByIdUser(idUser, 7); + if (roles is null) + return Enumerable.Empty(); + var permissions = roles + .Where(r => r.Permissions is not null) + .SelectMany(r => r.Permissions); + + return permissions; + } + + public bool HasPermission(int idUser, string permissionName) + { + if (idUser == 1) + return true; + + var relationsToRoles = GetCachRelationUserUserRoleCacheTag() + .Where(r => r.IdUser == idUser); + if (relationsToRoles is null) + return false; + + return userRoleRepository.HasPermission(relationsToRoles + .Select(r => r.IdUserRole), permissionName); + } + + private IEnumerable GetRolesNamesByIdUser(int idUser) + => GetRolesByIdUser(idUser, 7) + .Select(r => r.Caption) + .Distinct(); + + private async Task AssertLoginIsBusyAsync(string login, CancellationToken token = default) + { + var existingUserDto = (await GetCacheUserAsync(token)) + .FirstOrDefault(u => u.Login.ToLower() == login.ToLower()); + + if (existingUserDto is not null) + throw new ArgumentInvalidException($"Login {login} is busy by {existingUserDto.MakeDisplayName()}, id{existingUserDto.Id}", nameof(login)); + } + + private Task> GetCacheUserAsync(CancellationToken token) + => dbContext.Users + .Include(r => r.Company) + .Include(r => r.RelationUsersUserRoles) + .FromCacheAsync(userCacheTag, cacheObsolence, Convert, token); + private IEnumerable GetCacheUser() + => dbContext.Users + .Include(r => r.Company) + .Include(r => r.RelationUsersUserRoles) + .FromCache(userCacheTag, cacheObsolence, Convert); + private void DropCacheUsers() + => dbContext.Users.DropCache(userCacheTag); + + private Task> GetCacheRelationUserUserRoleAsync(CancellationToken token) + => dbContext.RelationUserUserRoles + .Include(r => r.UserRole) + .Include(r => r.User) + .FromCacheAsync(relationUserUserRoleCacheTag, cacheObsolence, token); + private IEnumerable GetCachRelationUserUserRoleCacheTag() + => dbContext.RelationUserUserRoles + .Include(r => r.UserRole) + .Include(r => r.User) + .FromCache(relationUserUserRoleCacheTag, cacheObsolence); + private void DropCacheRelationUserUserRoleCacheTag() + => dbContext.RelationUserUserRoles.DropCache(relationUserUserRoleCacheTag); + + private async Task UpdateRolesCacheForUserAsync(int idUser, IEnumerable newRoles, CancellationToken token) + { + var relations = (await GetCacheRelationUserUserRoleAsync(token)).Where(r => r.IdUser == idUser); + dbContext.RelationUserUserRoles.RemoveRange(relations); + + if (newRoles?.Any() == true) + await dbContext.RelationUserUserRoles.AddRangeAsync(newRoles.Select(role => new RelationUserUserRole + { + IdUser = idUser, + IdUserRole = role.Id + }), token).ConfigureAwait(false); + + await dbContext.SaveChangesAsync(token); + DropCacheRelationUserUserRoleCacheTag(); + } + + protected virtual User Convert(UserExtendedDto dto) + { + var entity = dto.Adapt(userTypeAdapterConfig); + if (string.IsNullOrEmpty(entity.PasswordHash)) + entity.PasswordHash = dbContext.Users.FirstOrDefault(u => u.Id == dto.Id)?.PasswordHash; + return entity; + } + + protected virtual UserExtendedDto Convert(User entity) + { + var dto = entity.Adapt(); + return dto; + } + } +#nullable disable +} diff --git a/AsbCloudInfrastructure/Repository/UserRoleRepository.cs b/AsbCloudInfrastructure/Repository/UserRoleRepository.cs new file mode 100644 index 00000000..2ca837d3 --- /dev/null +++ b/AsbCloudInfrastructure/Repository/UserRoleRepository.cs @@ -0,0 +1,293 @@ +using AsbCloudApp.Comparators; +using AsbCloudApp.Data; +using AsbCloudApp.Exceptions; +using AsbCloudApp.Repositories; +using AsbCloudDb; +using AsbCloudDb.Model; +using AsbCloudInfrastructure.EfCache; +using Mapster; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudInfrastructure.Repository +{ +#nullable enable + public class UserRoleRepository : IUserRoleRepository + { + private readonly IAsbCloudDbContext dbContext; + private const string userRoleCacheTag = "UserRole"; + private const string relationUserRoleUserRoleCacheTag = "RelationUserRoleUserRole"; + private const string relationUserRolePermissionsCacheTag = "RelationUserRolePermissions"; + private static readonly TimeSpan relationCacheObsolence = TimeSpan.FromMinutes(15); + + public UserRoleRepository(IAsbCloudDbContext dbContext) + { + this.dbContext = dbContext; + } + + public async Task InsertAsync(UserRoleDto dto, CancellationToken token) + { + var entity = dto.Adapt(); + var updatedEntity = await dbContext.UserRoles.AddAsync(entity, token) + .ConfigureAwait(false); + dto.Id = updatedEntity.Entity.Id; + await UpdatePermissionsAsync(dto, token); + await UpdateIncludedRolesAsync(dto, token); + + await dbContext.SaveChangesAsync(token); + DropCacheUserRole(); + return updatedEntity.Entity.Id; + } + + public Task InsertRangeAsync(IEnumerable newItems, CancellationToken token) + { + throw new NotImplementedException(); + } + + public async Task> GetAllAsync(CancellationToken token) + { + var entities = await GetCacheUserRoleAsync(token) + .ConfigureAwait(false); + + return entities.Select(Convert); + + } + + public UserRoleDto? GetOrDefault(int id) + { + var entity = GetCacheUserRole().FirstOrDefault(x => x.Id == id); + if (entity is null) + return null; + return Convert(entity); + } + + public async Task GetOrDefaultAsync(int id, CancellationToken token) + { + var entity = (await GetCacheUserRoleAsync(token) + .ConfigureAwait(false)).FirstOrDefault(r => r.Id == id); + if (entity is null) + return null; + return Convert(entity); + } + + public async Task> GetByNamesAsync(IEnumerable names, CancellationToken token) + { + if (names?.Any() != true) + return Enumerable.Empty(); + + var entities = (await GetCacheUserRoleAsync(token)) + .Where(r => names.Contains(r.Caption)); + + if (entities?.Count() != names.Count()) + throw new ArgumentInvalidException("Invalid role names", nameof(names)); + + return entities.Select(Convert); + } + + public async Task UpdateAsync(UserRoleDto dto, CancellationToken token) + { + var entity = Convert(dto); + await UpdatePermissionsAsync(dto, token); + await UpdateIncludedRolesAsync(dto, token); + + var result = dbContext.UserRoles.Upsert(entity); + await dbContext.SaveChangesAsync(token); + DropCacheUserRole(); + return result.Entity.Id; + } + + public IEnumerable GetNestedById(int id, int recursionLevel = 7) + { + var role = GetCacheUserRole() + .FirstOrDefault(r => r.Id == id); + if (role is null) + return Enumerable.Empty(); + + var roles = new SortedSet(ComparerIId.GetInstance()) { Convert(role) }; + + if (recursionLevel <= 0 || role.RelationUserRoleUserRoles?.Any() != true) + return roles; + + foreach (var relation in role.RelationUserRoleUserRoles) + { + var nestedRoles = GetNestedById(relation.IdInclude, --recursionLevel); + if (nestedRoles?.Any() != true) + continue; + foreach (var nestedRole in nestedRoles) + roles.Add(nestedRole); + } + return roles; + } + + public async Task DeleteAsync(int id, CancellationToken token) + { + var entity = (await GetCacheUserRoleAsync(token)).FirstOrDefault(r => r.Id == id); + + if (entity is not null) + { + var removeEntity = dbContext.UserRoles.Remove(entity); + await dbContext.SaveChangesAsync(token); + DropCacheUserRole(); + return removeEntity.Entity.Id; + } + else return 0; + } + + public bool HasPermission(IEnumerable rolesIds, string permissionName) + { + var permissionInfo = GetCacheRelationUserRolePermissions() + .FirstOrDefault(p => p. Permission?.Name.ToLower() == permissionName.ToLower()) + ?.Permission; + + if (permissionInfo is null) + return false; + + if (rolesIds.Contains(1)) + return true; + + var idPermissionInfo = permissionInfo.Id; + var entities = GetCacheUserRole() + .Where(r => rolesIds.Contains(r.Id)); + + foreach (var role in entities) + if (HasPermission(role, idPermissionInfo)) + return true; + return false; + } + + private bool HasPermission(UserRole userRole, int idPermission, int recursionLevel = 7) + { + if (userRole.RelationUserRolePermissions.Any(p => p.IdPermission == idPermission)) + return true; + + if (recursionLevel <= 0 || userRole.RelationUserRoleUserRoles?.Any() != true) + return false; + + foreach (var relation in userRole.RelationUserRoleUserRoles) + { + var entity = GetCacheUserRole() + .First(p => p.Id == relation.IdInclude); + if (HasPermission(entity, idPermission, --recursionLevel)) + return true; + } + return false; + } + + private async Task UpdateIncludedRolesAsync(UserRoleDto dto, CancellationToken token) + { + if (dto?.Roles is null) + return; + + var relations = (await GetCacheRelationUserRoleUserRoleAsync(token).ConfigureAwait(false)) + .Where(r => r.Id == dto.Id); + + dbContext.RelationUserRoleUserRoles.RemoveRange(relations); + + if (dto.Roles.Any()) + { + var newRelations = dto.Roles.Select(r => new RelationUserRoleUserRole { Id = dto.Id, IdInclude = r.Id }); + await dbContext.RelationUserRoleUserRoles.AddRangeAsync(newRelations, token); + await dbContext.SaveChangesAsync(token); + } + DropCacheRelationUserRoleUserRole(); + } + + private async Task UpdatePermissionsAsync(UserRoleDto dto, CancellationToken token) + { + if (dto?.Permissions is null) + return; + + var relations = (await GetCacheRelationUserRolePermissionsAsync(token).ConfigureAwait(false)) + .Where(r => r.IdUserRole == dto.Id); + + dbContext.RelationUserRolePermissions.RemoveRange(relations); + + if (dto.Permissions.Any()) + { + var newRelations = dto.Permissions.Select(p => new RelationUserRolePermission + { + IdPermission = p.Id, + IdUserRole = dto.Id, + }); + + await dbContext.RelationUserRolePermissions.AddRangeAsync(newRelations, token); + await dbContext.SaveChangesAsync(token); + } + DropCacheRelationUserRolePermissions(); + } + + private Task> GetCacheUserRoleAsync(CancellationToken token) + => dbContext.UserRoles + .Include(r => r.RelationUserRolePermissions) + .Include(r => r.RelationUserRoleUserRoles) + .Include(r => r.RelationUsersUserRoles) + .FromCacheAsync(userRoleCacheTag, relationCacheObsolence, token); + private IEnumerable GetCacheUserRole() + => dbContext.UserRoles + .Include(r => r.RelationUserRolePermissions) + .Include(r => r.RelationUserRoleUserRoles) + .Include(r => r.RelationUsersUserRoles) + .FromCache(userRoleCacheTag, relationCacheObsolence); + private void DropCacheUserRole() + => dbContext.RelationUserUserRoles.DropCache(userRoleCacheTag); + + private Task> GetCacheRelationUserRoleUserRoleAsync(CancellationToken token) + => dbContext.RelationUserRoleUserRoles + .Include(r => r.IncludeRole) + .Include(r => r.Role) + .FromCacheAsync(relationUserRoleUserRoleCacheTag, relationCacheObsolence, token); + private void DropCacheRelationUserRoleUserRole() + => dbContext.RelationUserUserRoles.DropCache(relationUserRoleUserRoleCacheTag); + + private Task> GetCacheRelationUserRolePermissionsAsync(CancellationToken token) + => dbContext.RelationUserRolePermissions + .Include(r => r.UserRole) + .Include(r => r.Permission) + .FromCacheAsync(relationUserRolePermissionsCacheTag, relationCacheObsolence, token); + private IEnumerable GetCacheRelationUserRolePermissions() + => dbContext.RelationUserRolePermissions + .Include(r => r.UserRole) + .Include(r => r.Permission) + .FromCache(relationUserRolePermissionsCacheTag, relationCacheObsolence); + private void DropCacheRelationUserRolePermissions() + => dbContext.RelationUserRolePermissions.DropCache(relationUserRolePermissionsCacheTag); + + private UserRoleDto Convert(UserRole entity) + { + var dto = entity.Adapt(); + if (entity.RelationUserRolePermissions?.Any() == true) + { + dto.Permissions = GetCacheRelationUserRolePermissions() + .Where(r => entity.Id == r.IdUserRole) + .Select(r => Convert(r.Permission)); + } + + if (entity.RelationUserRoleUserRoles?.Any() == true) + { + var rolesCache = GetCacheUserRole(); + dto.Roles = entity.RelationUserRoleUserRoles + .Select(rel => Convert(rolesCache + .First(r => r.Id == rel.IdInclude))) + .ToArray(); + } + return dto; + } + + private static PermissionDto Convert(Permission entity) + { + var dto = entity.Adapt(); + return dto; + } + + private static UserRole Convert(UserRoleDto dto) + { + var entity = dto.Adapt(); + return entity; + } + } +#nullable disable +} diff --git a/AsbCloudInfrastructure/Res/logo_32.png b/AsbCloudInfrastructure/Res/logo_32.png deleted file mode 100644 index 827f705d..00000000 Binary files a/AsbCloudInfrastructure/Res/logo_32.png and /dev/null differ diff --git a/AsbCloudInfrastructure/Services/AuthService.cs b/AsbCloudInfrastructure/Services/AuthService.cs index bf81ed4b..385a4a1a 100644 --- a/AsbCloudInfrastructure/Services/AuthService.cs +++ b/AsbCloudInfrastructure/Services/AuthService.cs @@ -1,4 +1,5 @@ using AsbCloudApp.Data; +using AsbCloudApp.Repositories; using AsbCloudApp.Services; using AsbCloudDb.Model; using Mapster; @@ -21,7 +22,7 @@ namespace AsbCloudInfrastructure.Services public class AuthService : IAuthService { private readonly IAsbCloudDbContext db; - private readonly IUserService userService; + private readonly IUserRepository userRepository; public const string issuer = "a"; public const string audience = "a"; @@ -36,10 +37,10 @@ namespace AsbCloudInfrastructure.Services private readonly HashAlgorithm hashAlgorithm; private readonly Random rnd; - public AuthService(IAsbCloudDbContext db, IUserService userService) + public AuthService(IAsbCloudDbContext db, IUserRepository userRepository) { this.db = db; - this.userService = userService; + this.userRepository = userRepository; hashAlgorithm = SHA384.Create(); rnd = new Random((int)(DateTime.Now.Ticks % 2147480161)); } @@ -165,12 +166,12 @@ namespace AsbCloudInfrastructure.Services if (identity is null || user.IdState == 0) return null; - var userDto = await userService.GetOrDefaultAsync(user.Id, token); + var userDto = await userRepository.GetOrDefaultAsync(user.Id, token); if (userDto is null) return null; var dto = userDto.Adapt(); - dto.Permissions = userService.GetNestedPermissions(userDto.Id); + dto.Permissions = userRepository.GetNestedPermissions(userDto.Id); dto.Token = MakeToken(identity.Claims); return dto; } @@ -209,7 +210,7 @@ namespace AsbCloudInfrastructure.Services new (ClaimsIdentity.DefaultNameClaimType, user.Login), new (claimNameIdCompany, user.IdCompany?.ToString()!), }; - var roles = userService.GetRolesByIdUser(user.Id); + var roles = userRepository.GetRolesByIdUser(user.Id); if (roles is not null) foreach (var role in roles) claims.Add(new Claim(ClaimsIdentity.DefaultRoleClaimType, role.Caption)); diff --git a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs index 3e8b3df2..87decac8 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs @@ -18,7 +18,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations #nullable enable public class DetectedOperationService : IDetectedOperationService { - public const int IdOperationRotor = 1; + public const int IdOperationRotor = 2; public const int IdOperationSlide = 3; public const int IdOperationSlipsTime = 14; public const int idOperationFlushing = 22; @@ -82,6 +82,30 @@ namespace AsbCloudInfrastructure.Services.DetectOperations var schedules = await scheduleService.GetByIdWellAsync(request.IdWell, token); var dtos = data.Select(o => Convert(o, well, operationValues, schedules)); return dtos; + } + + public async Task> GetDepthIntervalAllOperationsAsync(IEnumerable telemetryIds, DateTimeOffset gtDate, DateTimeOffset ltDate, CancellationToken token) + { + var query = db.Set() + .Include(o => o.OperationCategory) + .Where(o => o.DateStart >= gtDate) + .Where(o => o.DateEnd <= ltDate) + .Where(o => telemetryIds.Contains(o.IdTelemetry)) + .GroupBy(g => g.IdTelemetry) + .Select(g => new + { + IdTelemetry = g.Key, + RotorDepthInterval = g.Where(o => o.IdCategory == IdOperationRotor).Sum(o => o.DepthEnd - o.DepthStart), + SlideDepthInterval = g.Where(o => o.IdCategory == IdOperationSlide).Sum(o => o.DepthEnd - o.DepthStart) + }); + var data = await query.ToArrayAsync(token); + var result = data.Select(g => + ( + g.IdTelemetry, + g.RotorDepthInterval, + g.SlideDepthInterval + )); + return result; } private static IEnumerable GetOperationsDrillersStat(IEnumerable operations) @@ -301,6 +325,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations var exportService = new DetectedOperationExportService(db, wellService); return exportService.ExportAsync(idsWells, token); } + } #nullable disable } diff --git a/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs b/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs index e926a886..b1a6d0c6 100644 --- a/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs +++ b/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs @@ -1,5 +1,6 @@ using AsbCloudApp.Data; using AsbCloudApp.Exceptions; +using AsbCloudApp.Repositories; using AsbCloudApp.Services; using AsbCloudDb.Model; using AsbCloudInfrastructure.Repository; @@ -21,7 +22,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram private readonly IAsbCloudDbContext context; private readonly FileService fileService; - private readonly IUserService userService; + private readonly IUserRepository userRepository; private readonly IWellService wellService; private readonly IConfiguration configuration; private readonly IBackgroundWorkerService backgroundWorker; @@ -51,7 +52,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram public DrillingProgramService( IAsbCloudDbContext context, FileService fileService, - IUserService userService, + IUserRepository userRepository, IWellService wellService, IConfiguration configuration, IBackgroundWorkerService backgroundWorker, @@ -59,7 +60,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram { this.context = context; this.fileService = fileService; - this.userService = userService; + this.userRepository = userRepository; this.wellService = wellService; this.configuration = configuration; this.backgroundWorker = backgroundWorker; @@ -127,7 +128,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram Parts = parts, Program = files.FirstOrDefault(f => f.IdCategory == idFileCategoryDrillingProgram) .Adapt(), - PermissionToEdit = userService.HasPermission(idUser, "DrillingProgram.edit"), + PermissionToEdit = userRepository.HasPermission(idUser, "DrillingProgram.edit"), }; if (parts.Any()) @@ -226,7 +227,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram public async Task AddUserAsync(int idWell, int idFileCategory, int idUser, int idUserRole, CancellationToken token = default) { - var user = await userService.GetOrDefaultAsync(idUser, token); + var user = await userRepository.GetOrDefaultAsync(idUser, token); if (user is null) throw new ArgumentInvalidException($"User id == {idUser} does not exist", nameof(idUser)); diff --git a/AsbCloudInfrastructure/Services/FileCategoryService.cs b/AsbCloudInfrastructure/Services/FileCategoryService.cs index 3bb8326e..2b30583e 100644 --- a/AsbCloudInfrastructure/Services/FileCategoryService.cs +++ b/AsbCloudInfrastructure/Services/FileCategoryService.cs @@ -15,7 +15,7 @@ namespace AsbCloudInfrastructure.Services : base(context) { } - + //TODO: Перенести в сервис "дело скважины" public async Task> GetWellCaseCategoriesAsync(CancellationToken token) { var cache = await GetCacheAsync(token) diff --git a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs index cbef2343..ada2ab42 100644 --- a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs +++ b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs @@ -25,6 +25,10 @@ namespace AsbCloudInfrastructure.Services.Subsystems private readonly IWellService wellService; private readonly ICrudService subsystemService; private readonly IDetectedOperationService detectedOperationService; + public const int IdSubsystemAKB = 1; + public const int IdSubsystemMSE = 2; + public const int IdSubsystemSpin = 65536; + public const int IdSubsystemTorque = 65537; public SubsystemOperationTimeService(IAsbCloudDbContext db, IWellService wellService, ICrudService subsystemService, IDetectedOperationService detectedOperationService) { this.db = db; @@ -75,7 +79,7 @@ namespace AsbCloudInfrastructure.Services.Subsystems data = Trim(data, begin, end); } - var dtos = data.Select(o => Convert(o, well)); + var dtos = data.Select(o => Convert(o, well.Timezone.Hours)); return dtos; } @@ -101,7 +105,7 @@ namespace AsbCloudInfrastructure.Services.Subsystems return null; var depthInterval = GetDepthInterval(detectedOperations); - var statList = CalcStat(data,depthInterval,request, token); + var statList = CalcStat(data,depthInterval); return statList; } @@ -125,7 +129,7 @@ namespace AsbCloudInfrastructure.Services.Subsystems return items; } - private IEnumerable CalcStat(IEnumerable dtos, (double depthIntervalRotor, double depthIntervalSlide) depthInterval, SubsystemOperationTimeRequest request, CancellationToken token) + private IEnumerable CalcStat(IEnumerable dtos, (double depthIntervalRotor, double depthIntervalSlide) depthInterval) { var groupedDataSubsystems = dtos .GroupBy(o => o.IdSubsystem); @@ -152,9 +156,8 @@ namespace AsbCloudInfrastructure.Services.Subsystems return result; } - private (double depthIntervalRotor, double depthIntervalSlide) GetDepthInterval (IEnumerable detectedOperations) - { - + private static (double depthIntervalRotor, double depthIntervalSlide) GetDepthInterval (IEnumerable detectedOperations) + { var depthIntervalRotor = detectedOperations.Where(o => o.IdCategory == 1) .Sum(o => o.DepthEnd - o.DepthStart); var depthIntervalSlide = detectedOperations.Where(o => o.IdCategory == 3) @@ -163,27 +166,96 @@ namespace AsbCloudInfrastructure.Services.Subsystems return depthInterval; } - private double GetDepthIntervalSubsystem(int idSubsystem, (double depthIntervalRotor, double depthIntervalSlide) depthInterval) + private static double GetDepthIntervalSubsystem(int idSubsystem, (double depthIntervalRotor, double depthIntervalSlide) depthInterval) { var depthIntervalSubsystem = 0d; //AKB - MSE - if (idSubsystem == 1 | idSubsystem == 2) + if (idSubsystem == IdSubsystemAKB | idSubsystem == IdSubsystemMSE) { depthIntervalSubsystem = depthInterval.depthIntervalRotor + depthInterval.depthIntervalSlide; } //Spin - if (idSubsystem == 65536) + if (idSubsystem == IdSubsystemSpin) { depthIntervalSubsystem = depthInterval.depthIntervalSlide; } //Torque - if (idSubsystem == 65537) + if (idSubsystem == IdSubsystemTorque) { depthIntervalSubsystem = depthInterval.depthIntervalRotor; } return depthIntervalSubsystem; } + + private async Task> GetActiveWellsByCompany(int idCompany, CancellationToken token) + { + var listWell = await wellService.GetWellsByCompanyAsync(idCompany, token); + var active = listWell.Where(w => w.IdState == 1); + return active; + } + + /// + public async Task> GetStatByActiveWells(int idCompany, DateTime? gtDate, DateTime? ltDate, CancellationToken token) + { + var wells = await GetActiveWellsByCompany(idCompany, token); + if (!wells.Any()) + return Enumerable.Empty(); + + var hoursOffset = wells + .FirstOrDefault(well => well.Timezone is not null) + ?.Timezone.Hours + ?? 5d; + + var beginUTC = gtDate.HasValue + ? gtDate.Value.ToUtcDateTimeOffset(hoursOffset) + :DateTime.Today.AddDays(-1).ToUtcDateTimeOffset(hoursOffset); + + var endUTC = ltDate.HasValue + ? ltDate.Value.ToUtcDateTimeOffset(hoursOffset) + : DateTime.Today.ToUtcDateTimeOffset(hoursOffset); + + var telemetryIds = wells + .Where(w => w.IdTelemetry is not null) + .Select(w => w.IdTelemetry) + .Distinct(); + + var query = db.SubsystemOperationTimes + .Where(o => telemetryIds.Contains(o.IdTelemetry) && + o.DateStart >= beginUTC && + o.DateEnd <= endUTC) + .AsNoTracking(); + + var subsystemsOperationTime = await query.ToListAsync(token); + + var depthIntervals = await detectedOperationService + .GetDepthIntervalAllOperationsAsync(telemetryIds, beginUTC, endUTC, token); + + var result = wells + .Select(well => { + var dtos = subsystemsOperationTime + .Where(s => s.IdTelemetry == well.IdTelemetry) + .Select(s => Convert(s, well.Timezone.Hours)); + + var (idTelemetry, depthIntervalRotor, depthIntervalSlide) = depthIntervals + .FirstOrDefault(i => i.idTelemetry == well.IdTelemetry); + + var subsystemStat = idTelemetry > 0 && dtos.Any() + ? CalcStat(dtos, (depthIntervalRotor, depthIntervalSlide)) + : Enumerable.Empty(); + + return new SubsystemActiveWellStatDto + { + Well = well, + SubsystemAKB = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemAKB), + SubsystemMSE = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemMSE), + SubsystemSpinMaster = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemSpin), + SubsystemTorqueMaster = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemTorque), + }; + }); + + return result; + } /// public async Task GetDateRangeOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token) @@ -261,11 +333,12 @@ namespace AsbCloudInfrastructure.Services.Subsystems return query; } - private static SubsystemOperationTimeDto Convert(SubsystemOperationTime operationTime, WellDto well) + private static SubsystemOperationTimeDto Convert(SubsystemOperationTime operationTime, double? timezoneHours = null) { var dto = operationTime.Adapt(); - dto.DateStart = operationTime.DateStart.ToRemoteDateTime(well.Timezone.Hours); - dto.DateEnd = operationTime.DateEnd.ToRemoteDateTime(well.Timezone.Hours); + var hours = timezoneHours ?? operationTime.Telemetry.TimeZone.Hours; + dto.DateStart = operationTime.DateStart.ToRemoteDateTime(hours); + dto.DateEnd = operationTime.DateEnd.ToRemoteDateTime(hours); return dto; } diff --git a/AsbCloudInfrastructure/Services/UserRoleService.cs b/AsbCloudInfrastructure/Services/UserRoleService.cs deleted file mode 100644 index 37f05d8c..00000000 --- a/AsbCloudInfrastructure/Services/UserRoleService.cs +++ /dev/null @@ -1,234 +0,0 @@ -using AsbCloudApp.Comparators; -using AsbCloudApp.Data; -using AsbCloudApp.Exceptions; -using AsbCloudApp.Services; -using AsbCloudDb.Model; -using AsbCloudInfrastructure.Services.Cache; -using Mapster; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace AsbCloudInfrastructure.Services -{ - public class UserRoleService : IUserRoleService - { - private readonly CacheTable cacheUserRoles; - private readonly CacheTable cacheRelationUserRolePermissions; - private readonly CacheTable cacheRelationUserRoleUserRole; - - public ISet Includes { get; } = new SortedSet(); - - public UserRoleService(IAsbCloudDbContext context, CacheDb cacheDb) - { - cacheUserRoles = cacheDb.GetCachedTable((AsbCloudDbContext)context, nameof(UserRole.RelationUserRolePermissions), nameof(UserRole.RelationUserRoleUserRoles)); - cacheRelationUserRolePermissions = cacheDb.GetCachedTable((AsbCloudDbContext)context, nameof(RelationUserRolePermission.Permission)); - cacheRelationUserRoleUserRole = cacheDb.GetCachedTable((AsbCloudDbContext)context, nameof(RelationUserRoleUserRole.IncludeRole), nameof(RelationUserRoleUserRole.Role)); - } - - public async Task InsertAsync(UserRoleDto dto, CancellationToken token = default) - { - var entity = dto.Adapt(); - var updatedEntity = await cacheUserRoles.InsertAsync(entity, token) - .ConfigureAwait(false); - dto.Id = updatedEntity.Id; - await UpdatePermissionsAsync(dto, token); - await UpdateIncludedRolesAsync(dto, token); - - await cacheUserRoles.RefreshAsync(true, token) - .ConfigureAwait(false); - return updatedEntity?.Id ?? 0; - } - - public Task InsertRangeAsync(IEnumerable dtos, CancellationToken token = default) - { - throw new NotImplementedException(); - //var entities = dtos.Adapt(); - //return await cacheUserRoles.InsertAsync(entities, token).ConfigureAwait(false); - } - - public async Task> GetAllAsync(CancellationToken token = default) - { - var entities = await cacheUserRoles.WhereAsync(token) - .ConfigureAwait(false); - var dtos = entities?.Select(Convert); - return dtos; - } - public UserRoleDto GetOrDefault(int id) - { - var entity = cacheUserRoles.FirstOrDefault(r => r.Id == id); - if (entity is null) - return null; - var dto = Convert(entity); - return dto; - } - - public async Task GetOrDefaultAsync(int id, CancellationToken token = default) - { - var entity = await cacheUserRoles.FirstOrDefaultAsync(r => r.Id == id, token) - .ConfigureAwait(false); - if (entity is null) - return null; - var dto = Convert(entity); - return dto; - } - - public async Task> GetByNamesAsync(IEnumerable names, CancellationToken token = default) - { - if (names?.Any() != true) - return null; - var entities = await cacheUserRoles.WhereAsync(r => names.Contains(r.Caption), token) - .ConfigureAwait(false); - if (entities?.Count() != names.Count()) - throw new ArgumentInvalidException("Invalid role names", nameof(names)); - var dtos = entities.Select(Convert); - return dtos; - } - - public async Task UpdateAsync(UserRoleDto dto, CancellationToken token = default) - { - var entity = Convert(dto); - await UpdatePermissionsAsync(dto, token); - await UpdateIncludedRolesAsync(dto, token); - - var result = await cacheUserRoles.UpsertAsync(entity, token) - .ConfigureAwait(false); - return result; - } - - public IEnumerable GetNestedById(int id, int recursionLevel = 7) - { - var role = cacheUserRoles.FirstOrDefault(r => r.Id == id); - if (role is null) - return null; - var dto = Convert(role); - var roles = new SortedSet(ComparerIId.GetInstance()) { dto }; - - if (recursionLevel <= 0 || role.RelationUserRoleUserRoles?.Any() != true) - return roles; - - foreach (var relation in role.RelationUserRoleUserRoles) - { - var nestedRoles = GetNestedById(relation.IdInclude, --recursionLevel); - if (nestedRoles?.Any() != true) - continue; - foreach (var nestedRole in nestedRoles) - roles.Add(nestedRole); - } - return roles; - } - - private async Task UpdatePermissionsAsync(UserRoleDto dto, CancellationToken token) - { - if (dto?.Permissions is null) - return; - - await cacheRelationUserRolePermissions.RemoveAsync(r => r.IdUserRole == dto.Id, token) - .ConfigureAwait(false); - - if (dto.Permissions.Any()) - { - var newRelationRoleToPermission = dto.Permissions.Select(p => new RelationUserRolePermission - { - IdPermission = p.Id, - IdUserRole = dto.Id, - }); - - await cacheRelationUserRolePermissions.InsertAsync(newRelationRoleToPermission, token) - .ConfigureAwait(false); - } - } - - private async Task UpdateIncludedRolesAsync(UserRoleDto dto, CancellationToken token) - { - if (dto?.Roles is null) - return; - - await cacheRelationUserRoleUserRole.RemoveAsync(rel => rel.Id == dto.Id, token); - - if (dto.Roles.Any()) - { - var newRelations = dto.Roles.Select(r => new RelationUserRoleUserRole { Id = dto.Id, IdInclude = r.Id }); - await cacheRelationUserRoleUserRole.UpsertAsync(newRelations, token); - } - } - - public Task DeleteAsync(int id, CancellationToken token = default) - => cacheUserRoles.RemoveAsync(r => r.Id == id, token); - - public Task DeleteAsync(IEnumerable ids, CancellationToken token = default) - => cacheUserRoles.RemoveAsync(r => ids.Contains(r.Id), token); - - public bool HasPermission(IEnumerable rolesIds, string permissionName) - { - var permissionInfo = cacheRelationUserRolePermissions - .FirstOrDefault(p => p.Permission?.Name.ToLower() == permissionName.ToLower()) - ?.Permission; - - if (permissionInfo is null) - return false; - - if (rolesIds.Contains(1)) - return true; - - var idPermissionInfo = permissionInfo.Id; - var roles = cacheUserRoles.Where(r => rolesIds.Contains(r.Id)); - foreach (var role in roles) - if (HasPermission(role, idPermissionInfo)) - return true; - return false; - } - - private bool HasPermission(UserRole userRole, int idPermission, int recursionLevel = 7) - { - if (userRole.RelationUserRolePermissions.Any(p => p.IdPermission == idPermission)) - return true; - - if (recursionLevel <= 0 || userRole.RelationUserRoleUserRoles?.Any() != true) - return false; - - foreach (var relation in userRole.RelationUserRoleUserRoles) - { - var includedRole = cacheUserRoles.First(p => p.Id == relation.IdInclude); - if (HasPermission(includedRole, idPermission, --recursionLevel)) - return true; - } - return false; - } - - private static UserRole Convert(UserRoleDto dto) - { - var entity = dto.Adapt(); - return entity; - } - - private UserRoleDto Convert(UserRole entity) - { - var dto = entity.Adapt(); - if (entity.RelationUserRolePermissions?.Any() == true) - { - dto.Permissions = cacheRelationUserRolePermissions - .Where(r => entity.Id == r.IdUserRole) - .Select(r => Convert(r.Permission)); - } - - if (entity.RelationUserRoleUserRoles?.Any() == true) - { - dto.Roles = entity.RelationUserRoleUserRoles.Select(rel => - { - var includedRole = cacheUserRoles.First(r => r.Id == rel.IdInclude); - return Convert(includedRole); - }).ToList(); - } - return dto; - } - - private static PermissionDto Convert(Permission entity) - { - var dto = entity.Adapt(); - return dto; - } - } -} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/UserService.cs b/AsbCloudInfrastructure/Services/UserService.cs deleted file mode 100644 index c282204e..00000000 --- a/AsbCloudInfrastructure/Services/UserService.cs +++ /dev/null @@ -1,197 +0,0 @@ -using AsbCloudApp.Data; -using AsbCloudApp.Exceptions; -using AsbCloudApp.Services; -using AsbCloudDb.Model; -using AsbCloudInfrastructure.Services.Cache; -using Mapster; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace AsbCloudInfrastructure.Services -{ - public class UserService : IUserService - { - private readonly CacheTable cacheUsers; - private readonly CacheTable cacheRelationUserToRoles; - public ISet Includes { get; } = new SortedSet(); - public IUserRoleService RoleService { get; } - - private static readonly TypeAdapterConfig userTypeAdapterConfig = TypeAdapterConfig - .NewConfig() - .Ignore(dst => dst.Company, - dst => dst.FileMarks, - dst => dst.Files, - dst => dst.RelationUsersUserRoles) - .Config; - - public UserService(IAsbCloudDbContext context, CacheDb cacheDb, IUserRoleService roleService) - { - var db = (AsbCloudDbContext)context; - cacheUsers = cacheDb.GetCachedTable( - db, - new[] { - nameof(User.RelationUsersUserRoles), - nameof(User.Company), - }); - cacheRelationUserToRoles = cacheDb.GetCachedTable( - db, - new[] { - nameof(RelationUserUserRole.User), - nameof(RelationUserUserRole.UserRole), - }); - RoleService = roleService; - } - - public async Task InsertAsync(UserExtendedDto dto, CancellationToken token = default) - { - dto.Id = default; - var entity = Convert(dto); - await AssertLoginIsBusyAsync(dto.Login, token); - var userRoles = await RoleService.GetByNamesAsync(dto.RoleNames, token).ConfigureAwait(false); - var updatedEntity = await cacheUsers.InsertAsync(entity, token).ConfigureAwait(false); - if (userRoles?.Any() == true) - await UpdateRolesCacheForUserAsync(updatedEntity.Id, userRoles, token); - return updatedEntity?.Id ?? 0; - } - - private async Task AssertLoginIsBusyAsync(string login, CancellationToken token = default) - { - var existingUser = await cacheUsers.FirstOrDefaultAsync(u => u.Login.ToLower() == login.ToLower(), token); - if (existingUser is not null) - throw new ArgumentInvalidException($"Login {login} is busy by {existingUser.MakeDisplayName()}, id{existingUser.Id}", nameof(login)); - } - - public Task InsertRangeAsync(IEnumerable newItems, CancellationToken token = default) - { - throw new NotImplementedException(); - } - - public async Task> GetAllAsync(CancellationToken token = default) - { - var entities = (await cacheUsers.WhereAsync(token).ConfigureAwait(false)) - .ToList(); - if (entities.Count == 0) - return null; - var dtos = entities.Select(Convert).ToList(); - for (var i = 0; i < dtos.Count; i++) - dtos[i].RoleNames = GetRolesNamesByIdUser(dtos[i].Id); - return dtos; - } - - public UserExtendedDto GetOrDefault(int id) - { - var entity = cacheUsers.FirstOrDefault(u => u.Id == id); - var dto = Convert(entity); - dto.RoleNames = GetRolesNamesByIdUser(dto.Id); - return dto; - } - - public async Task GetOrDefaultAsync(int id, CancellationToken token = default) - { - var entity = await cacheUsers.FirstOrDefaultAsync(u => u.Id == id, token).ConfigureAwait(false); - var dto = Convert(entity); - dto.RoleNames = GetRolesNamesByIdUser(dto.Id); - return dto; - } - - public async Task UpdateAsync(UserExtendedDto dto, CancellationToken token = default) - { - if (dto.Id <= 1) - throw new ArgumentInvalidException($"Invalid id {dto.Id}. You can't edit this user.", nameof(dto)); - - var oldUser = await cacheUsers.FirstOrDefaultAsync(u => u.Id == dto.Id, token); - if (oldUser.Login != dto.Login) - await AssertLoginIsBusyAsync(dto.Login, token); - - var userRoles = await RoleService.GetByNamesAsync(dto.RoleNames, token).ConfigureAwait(false); - await UpdateRolesCacheForUserAsync(dto.Id, userRoles, token); - - var entity = Convert(dto); - - var result = await cacheUsers.UpsertAsync(entity, token) - .ConfigureAwait(false); - return result; - } - - public Task DeleteAsync(int id, CancellationToken token = default) - { - if (id <= 1) - return Task.FromResult(0); - return cacheUsers.RemoveAsync(r => r.Id == id, token); - } - - public Task DeleteAsync(IEnumerable ids, CancellationToken token = default) - { - var filteredIds = ids.Where(i => i > 1).ToList(); - return cacheUsers.RemoveAsync(r => filteredIds.Contains(r.Id), token); - } - - private IEnumerable GetRolesNamesByIdUser(int idUser) - => GetRolesByIdUser(idUser) - ?.Select(r => r.Caption) - .Distinct(); - - public IEnumerable GetRolesByIdUser(int idUser, int nestedLevel = 0) - { - var roles = cacheRelationUserToRoles.Where(r => r.IdUser == idUser); - if (roles?.Any() != true) - return null; - return roles.SelectMany(r => RoleService.GetNestedById(r.IdUserRole, nestedLevel)); - } - - public IEnumerable GetNestedPermissions(int idUser) - { - var roles = GetRolesByIdUser(idUser, 7); - if (roles?.Any() != true) - return null; - var permissions = roles - .Where(r => r.Permissions is not null) - .SelectMany(r => r.Permissions); - - return permissions; - } - - private async Task UpdateRolesCacheForUserAsync(int idUser, IEnumerable newRoles, CancellationToken token) - { - await cacheRelationUserToRoles.RemoveAsync(r => r.IdUser == idUser, token) - .ConfigureAwait(false); - - if (newRoles?.Any() == true) - await cacheRelationUserToRoles.InsertAsync(newRoles.Select(role => new RelationUserUserRole - { - IdUser = idUser, - IdUserRole = role.Id - }), token).ConfigureAwait(false); - } - - public bool HasPermission(int idUser, string permissionName) - { - if (idUser == 1) - return true; - - var relationsToRoles = cacheRelationUserToRoles.Where(r => r.IdUser == idUser); - if (relationsToRoles is null) - return false; - - return RoleService.HasPermission(relationsToRoles.Select(r => r.IdUserRole), - permissionName); - } - - protected virtual User Convert(UserExtendedDto dto) - { - var entity = dto.Adapt(userTypeAdapterConfig); - if (string.IsNullOrEmpty(entity.PasswordHash)) - entity.PasswordHash = cacheUsers.FirstOrDefault(u => u.Id == dto.Id)?.PasswordHash; - return entity; - } - - protected virtual UserExtendedDto Convert(User entity) - { - var dto = entity.Adapt(); - return dto; - } - } -} diff --git a/AsbCloudInfrastructure/Services/WellFinalDocumentsService.cs b/AsbCloudInfrastructure/Services/WellFinalDocumentsService.cs index 4d44f38c..c82b9f7a 100644 --- a/AsbCloudInfrastructure/Services/WellFinalDocumentsService.cs +++ b/AsbCloudInfrastructure/Services/WellFinalDocumentsService.cs @@ -1,5 +1,6 @@ using AsbCloudApp.Data; using AsbCloudApp.Exceptions; +using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudDb.Model; @@ -24,7 +25,7 @@ namespace AsbCloudInfrastructure.Services { private readonly IAsbCloudDbContext context; private readonly FileService fileService; - private readonly IUserService userService; + private readonly IUserRepository userRepository; private readonly IWellService wellService; private readonly IConfiguration configuration; private readonly IEmailService emailService; @@ -34,7 +35,7 @@ namespace AsbCloudInfrastructure.Services public WellFinalDocumentsService(IAsbCloudDbContext context, FileService fileService, - IUserService userService, + IUserRepository userRepository, IWellService wellService, IConfiguration configuration, IEmailService emailService, @@ -42,7 +43,7 @@ namespace AsbCloudInfrastructure.Services { this.context = context; this.fileService = fileService; - this.userService = userService; + this.userRepository = userRepository; this.wellService = wellService; this.configuration = configuration; this.emailService = emailService; @@ -120,7 +121,7 @@ namespace AsbCloudInfrastructure.Services var result = new WellCaseDto { IdWell = idWell, - PermissionToSetPubliher = userService.HasPermission(idUser, "WellFinalDocument.editPublisher"), + PermissionToSetPubliher = userRepository.HasPermission(idUser, "WellFinalDocument.editPublisher"), WellFinalDocuments = docs, }; return result; @@ -133,7 +134,7 @@ namespace AsbCloudInfrastructure.Services .ToListAsync(token) .ConfigureAwait(false); - var allUsers = await userService.GetAllAsync(token) + var allUsers = await userRepository.GetAllAsync(token) .ConfigureAwait(false); return allUsers.Where(x => { @@ -181,7 +182,7 @@ namespace AsbCloudInfrastructure.Services { foreach (var item in dtos) { - var user = await userService.GetOrDefaultAsync(item.IdUser, token); + var user = await userRepository.GetOrDefaultAsync(item.IdUser, token); if (user?.Email is not null) { var category = await fileCategoryService.GetOrDefaultAsync(item.IdCategory, token); diff --git a/AsbCloudWebApi.Tests/CacheTests/CacheTest.cs b/AsbCloudWebApi.Tests/CacheTests/CacheTest.cs new file mode 100644 index 00000000..f67a0a46 --- /dev/null +++ b/AsbCloudWebApi.Tests/CacheTests/CacheTest.cs @@ -0,0 +1,61 @@ +using AsbCloudApp.Data; +using AsbCloudApp.Services; +using AsbCloudDb.Model; +using AsbCloudInfrastructure.EfCache; +using DocumentFormat.OpenXml.Spreadsheet; +using Mapster; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace AsbCloudWebApi.Tests.CacheTests +{ + public class CacheTest + { + private readonly string userCacheTag = "Users"; + private static readonly TimeSpan cacheObsolence = TimeSpan.FromMinutes(15); + public IEnumerable UsersCache = new List { + new User { Id = 1, Name = "test1", Email = "test1@mail.com" }, + new User { Id = 2, Name = "test2", Email = "test2@mail.com" }, + new User { Id = 3, Name = "test3", Email = "test3@mail.com" }, + new User { Id = 4, Name = "test4", Email = "test4@mail.com" }, + new User { Id = 5, Name = "test5", Email = "test5@mail.com" }, + new User { Id = 6, Name = "test6", Email = "test6@mail.com" }, + new User { Id = 7, Name = "test7", Email = "test7@mail.com" } + }; + + [Fact] + public void Send_n_requests_cache_user() + { + const int iterations = 1230; + var cache = UsersCache.AsQueryable(); + var cacheCount = UsersCache.Count(); + var fail = false; + + var tasks = new int[iterations].Select(_=> new Task(() => + { + var data = cache.FromCache(userCacheTag, cacheObsolence, Convert).ToArray(); + if (data.Count() != cacheCount) + fail = true; + })) + .ToArray(); + + foreach(var task in tasks) + task.Start(); + + Task.WaitAll(tasks); + Assert.False(fail); + } + + private UserExtendedDto Convert(User entity) + { + var dto = entity.Adapt(); + return dto; + } + } +} diff --git a/AsbCloudWebApi.Tests/ServicesTests/DrillingProgramServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/DrillingProgramServiceTest.cs index a1e275b9..2c829eb4 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/DrillingProgramServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/DrillingProgramServiceTest.cs @@ -1,4 +1,5 @@ using AsbCloudApp.Data; +using AsbCloudApp.Repositories; using AsbCloudApp.Services; using AsbCloudDb.Model; using AsbCloudInfrastructure.Repository; @@ -79,7 +80,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests }; private readonly Mock fileServiceMock; - private readonly Mock userServiceMock; + private readonly Mock userRepositoryMock; private readonly Mock wellServiceMock; private readonly Mock configurationMock; private readonly Mock backgroundWorkerMock; @@ -98,7 +99,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests db.SaveChanges(); fileServiceMock = new Mock(); - userServiceMock = new Mock(); + userRepositoryMock = new Mock(); wellServiceMock = new Mock(); configurationMock = new Mock(); backgroundWorkerMock = new Mock(); @@ -110,7 +111,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests var service = new DrillingProgramService( db, fileServiceMock.Object, - userServiceMock.Object, + userRepositoryMock.Object, wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, @@ -127,7 +128,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests var service = new DrillingProgramService( db, fileServiceMock.Object, - userServiceMock.Object, + userRepositoryMock.Object, wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, @@ -146,7 +147,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests var service = new DrillingProgramService( db, fileServiceMock.Object, - userServiceMock.Object, + userRepositoryMock.Object, wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, @@ -163,13 +164,13 @@ namespace AsbCloudWebApi.Tests.ServicesTests db.DrillingProgramParts.Add(new DrillingProgramPart { IdFileCategory = 1001, IdWell = idWell }); db.SaveChanges(); - userServiceMock.Setup((s) => s.GetOrDefaultAsync(It.IsAny(), It.IsAny())) + userRepositoryMock.Setup((s) => s.GetOrDefaultAsync(It.IsAny(), It.IsAny())) .Returns(Task.FromResult(publisher1.Adapt())); var service = new DrillingProgramService( db, fileServiceMock.Object, - userServiceMock.Object, + userRepositoryMock.Object, wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, @@ -198,13 +199,13 @@ namespace AsbCloudWebApi.Tests.ServicesTests IdUserRole = idUserRole }); db.SaveChanges(); - userServiceMock.Setup((s) => s.GetOrDefaultAsync(It.IsAny(), It.IsAny())) + userRepositoryMock.Setup((s) => s.GetOrDefaultAsync(It.IsAny(), It.IsAny())) .Returns(Task.FromResult(publisher1.Adapt())); var service = new DrillingProgramService( db, fileServiceMock.Object, - userServiceMock.Object, + userRepositoryMock.Object, wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, @@ -230,7 +231,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests var service = new DrillingProgramService( db, fileServiceMock.Object, - userServiceMock.Object, + userRepositoryMock.Object, wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, @@ -261,7 +262,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests var service = new DrillingProgramService( db, fileServiceMock.Object, - userServiceMock.Object, + userRepositoryMock.Object, wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, @@ -299,7 +300,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests var service = new DrillingProgramService( db, fileServiceMock.Object, - userServiceMock.Object, + userRepositoryMock.Object, wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, @@ -326,7 +327,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests var service = new DrillingProgramService( db, fileServiceMock.Object, - userServiceMock.Object, + userRepositoryMock.Object, wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, @@ -353,7 +354,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests var service = new DrillingProgramService( db, fileServiceMock.Object, - userServiceMock.Object, + userRepositoryMock.Object, wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, @@ -383,7 +384,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests var service = new DrillingProgramService( db, fileServiceMock.Object, - userServiceMock.Object, + userRepositoryMock.Object, wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, diff --git a/AsbCloudWebApi.Tests/ServicesTests/UserRoleServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/UserRoleServiceTest.cs deleted file mode 100644 index 8ce1c1b3..00000000 --- a/AsbCloudWebApi.Tests/ServicesTests/UserRoleServiceTest.cs +++ /dev/null @@ -1,187 +0,0 @@ -using AsbCloudApp.Data; -using AsbCloudDb.Model; -using AsbCloudInfrastructure.Services; -using AsbCloudInfrastructure.Services.Cache; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - -namespace AsbCloudWebApi.Tests.ServicesTests -{ - public class UserRoleServiceTest - { - private readonly AsbCloudDbContext context; - private readonly CacheDb cacheDb; - - private readonly List roles = new() { - new UserRole { Id = 1_000_001, Caption = "role 1 level 0" }, - new UserRole { Id = 1_000_002, Caption = "role 2 level 1" }, - new UserRole { Id = 1_000_003, Caption = "role 3 level 1" }, - new UserRole { Id = 1_000_004, Caption = "role 4 level 2" }, - }; - - private readonly List relationRoleRole = new() - { - new RelationUserRoleUserRole { Id = 1_000_002, IdInclude = 1_000_001 }, - new RelationUserRoleUserRole { Id = 1_000_003, IdInclude = 1_000_001 }, - new RelationUserRoleUserRole { Id = 1_000_004, IdInclude = 1_000_002 }, - new RelationUserRoleUserRole { Id = 1_000_004, IdInclude = 1_000_003 }, - }; - - private readonly List permissions = new() - { - new Permission { Id = 2_000_001, Name = "permission 1" }, - new Permission { Id = 2_000_002, Name = "permission 2" }, - new Permission { Id = 2_000_003, Name = "permission 3" }, - new Permission { Id = 2_000_004, Name = "permission 4" }, - }; - - private readonly List relationRolePermission = new() - { - new RelationUserRolePermission { IdUserRole = 1_000_001, IdPermission = 2_000_001 }, - new RelationUserRolePermission { IdUserRole = 1_000_002, IdPermission = 2_000_002 }, - new RelationUserRolePermission { IdUserRole = 1_000_003, IdPermission = 2_000_003 }, - new RelationUserRolePermission { IdUserRole = 1_000_004, IdPermission = 2_000_004 }, - }; - - public UserRoleServiceTest() - { - cacheDb = new CacheDb(); - context = TestHelpter.MakeTestContext(); - context.UserRoles.RemoveRange(roles); - context.Permissions.RemoveRange(permissions); - context.SaveChanges(); - context.UserRoles.AddRange(roles); - context.Permissions.AddRange(permissions); - context.SaveChanges(); - context.RelationUserRoleUserRoles.AddRange(relationRoleRole); - context.RelationUserRolePermissions.AddRange(relationRolePermission); - context.SaveChanges(); - } - - ~UserRoleServiceTest() - { - context.UserRoles.RemoveRange(roles); - context.Permissions.RemoveRange(permissions); - context.RelationUserRoleUserRoles.RemoveRange(relationRoleRole); - context.RelationUserRolePermissions.RemoveRange(relationRolePermission); - context.SaveChanges(); - context.Dispose(); - } - - [Fact] - public void GetNestedById_return_4_items() - { - var service = new UserRoleService(context, cacheDb); - var nestedRoles = service.GetNestedById(1_000_004); - Assert.Equal(roles.Count, nestedRoles.Count()); - } - - [Fact] - public void HasPermission_return_true() - { - var service = new UserRoleService(context, cacheDb); - var result = service.HasPermission(new int[] { 1_000_004 }, "permission 1"); - Assert.True(result); - } - - [Fact] - public void HasPermission_return_false() - { - var service = new UserRoleService(context, cacheDb); - var result = service.HasPermission(new int[] { 1_000_003 }, "permission 2"); - Assert.False(result); - } - - [Fact] - public async Task InsertAsync_returns_id() - { - var service = new UserRoleService(context, cacheDb); - var newRole = new UserRoleDto - { - Caption = "new role", - IdType = 0, - }; - var id = await service.InsertAsync(newRole, CancellationToken.None); - Assert.NotEqual(0, id); - } - - [Fact] - public async Task InsertAsync_updates_relation_to_permission() - { - var service = new UserRoleService(context, cacheDb); - var newRole = new UserRoleDto - { - Caption = "new role", - IdType = 0, - Permissions = new[] { new PermissionDto { Id = 2_000_001 } }, - }; - var id = await service.InsertAsync(newRole, CancellationToken.None); - var entity = await service.GetOrDefaultAsync(id); - Assert.Equal(newRole.Permissions.Count(), entity.Permissions.Count()); - } - - [Fact] - public async Task InsertAsync_updates_relation_to_role() - { - var service = new UserRoleService(context, cacheDb); - var newRole = new UserRoleDto - { - Caption = "new role", - IdType = 0, - Roles = new[] { new UserRoleDto { Id = 1_000_001 } } - }; - var id = await service.InsertAsync(newRole, CancellationToken.None); - var entity = await service.GetOrDefaultAsync(id); - Assert.Equal(newRole.Roles.Count, entity.Roles.Count); - } - - [Fact] - public async Task UpdateAsync_returns_id() - { - var service = new UserRoleService(context, cacheDb); - const int updateId = 1_000_002; - var modRole = new UserRoleDto - { - Id = updateId, - Caption = "role 2 level 1" - }; - var id = await service.UpdateAsync(modRole, CancellationToken.None); - Assert.Equal(updateId, id); - } - - [Fact] - public async Task UpdateAsync_updates_relation_to_permission() - { - var service = new UserRoleService(context, cacheDb); - const int updateId = 1_000_002; - var modRole = new UserRoleDto - { - Id = updateId, - Caption = "role 2 level 1", - Permissions = new[] { new PermissionDto { Id = 2_000_001 } }, - }; - var id = await service.UpdateAsync(modRole, CancellationToken.None); - var entity = await service.GetOrDefaultAsync(id); - Assert.Equal(modRole.Permissions.Count(), entity.Permissions.Count()); - } - - [Fact] - public async Task UpdateAsync_updates_relation_to_role() - { - var service = new UserRoleService(context, cacheDb); - const int updateId = 1_000_002; - var modRole = new UserRoleDto - { - Id = updateId, - Caption = "role 2 level 1", - Roles = new[] { new UserRoleDto { Id = 1_000_001 } } - }; - var id = await service.UpdateAsync(modRole, CancellationToken.None); - var entity = await service.GetOrDefaultAsync(id); - Assert.Equal(modRole.Roles.Count(), entity.Roles.Count()); - } - } -} diff --git a/AsbCloudWebApi.Tests/ServicesTests/UserServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/UserServiceTest.cs deleted file mode 100644 index 1e3d1b3f..00000000 --- a/AsbCloudWebApi.Tests/ServicesTests/UserServiceTest.cs +++ /dev/null @@ -1,75 +0,0 @@ -using AsbCloudApp.Data; -using AsbCloudApp.Services; -using AsbCloudDb.Model; -using AsbCloudInfrastructure.Services; -using AsbCloudInfrastructure.Services.Cache; -using Moq; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - -namespace AsbCloudWebApi.Tests.ServicesTests -{ - public class UserServiceTest - { - private readonly AsbCloudDbContext context; - private readonly CacheDb cacheDb; - private readonly Mock roleService; - - public UserServiceTest() - { - context = TestHelpter.MakeTestContext(); - cacheDb = new CacheDb(); - roleService = new Mock(); - - context.Users.RemoveRange(context.Users.Where(u => u.Id > 1)); - context.SaveChanges(); - } - - ~UserServiceTest() - { - context.Users.RemoveRange(context.Users.Where(u => u.Id > 1)); - context.SaveChanges(); - } - - [Fact] - public async Task InsertAsync_returns_id() - { - var service = new UserService(context, cacheDb, roleService.Object); - var dto = new UserExtendedDto - { - Id = 0, - IdCompany = 1, - Email = "test@test.test", - Login = "test", - Name = "test", - Company = new CompanyDto { Caption = "test", Id = 1 }, - Patronymic = "test", - Phone = "test", - Position = "test", - Surname = "test", - - }; - var id = await service.InsertAsync(dto, CancellationToken.None); - - Assert.NotEqual(0, id); - } - - [Fact] - public async Task InsertAsync_busy_login_throws_exceprion() - { - var service = new UserService(context, cacheDb, roleService.Object); - var dto = new UserExtendedDto - { - Id = 5, - Email = "test@test.test", - Login = "test clone" - }; - - await service.InsertAsync(dto, CancellationToken.None); - await Assert.ThrowsAsync(() => service.InsertAsync(dto, CancellationToken.None)); - } - } -} diff --git a/AsbCloudWebApi.Tests/ServicesTests/WellFinalDocumentsServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/WellFinalDocumentsServiceTest.cs index 1718b92d..eb0d0a89 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/WellFinalDocumentsServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/WellFinalDocumentsServiceTest.cs @@ -10,6 +10,8 @@ using Xunit; using System.IO; using System.Collections.Generic; using System.Linq; +using AsbCloudApp.Repositories; +using AsbCloudInfrastructure.Repository; namespace AsbCloudWebApi.Tests.ServicesTests { @@ -18,7 +20,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests private readonly AsbCloudDbContext context; private WellFinalDocumentsService service; private readonly Mock fileServiceMock; - private readonly Mock userServiceMock; + private readonly Mock userRepositoryMock; private readonly Mock wellServiceMock; private readonly Mock configurationMock; private readonly Mock emailServiceMock; @@ -45,8 +47,8 @@ namespace AsbCloudWebApi.Tests.ServicesTests context.SaveChanges(); fileServiceMock = new Mock(); - userServiceMock = new Mock(); - userServiceMock.Setup(x => x.GetAllAsync(CancellationToken.None)).Returns(Task.Run(() => users.Select(x => (UserExtendedDto)x))); + userRepositoryMock = new Mock(); + userRepositoryMock.Setup(x => x.GetAllAsync(CancellationToken.None)).Returns(Task.Run(() => users.Select(x => (UserExtendedDto)x))); wellServiceMock = new Mock(); configurationMock = new Mock(); @@ -56,7 +58,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests service = new WellFinalDocumentsService( context: context, fileService: fileServiceMock.Object, - userService: userServiceMock.Object, + userRepository: userRepositoryMock.Object, wellService: wellServiceMock.Object, configuration: configurationMock.Object, emailService: emailServiceMock.Object, diff --git a/AsbCloudWebApi/Controllers/AdminUserController.cs b/AsbCloudWebApi/Controllers/AdminUserController.cs index e515d370..0ce262d0 100644 --- a/AsbCloudWebApi/Controllers/AdminUserController.cs +++ b/AsbCloudWebApi/Controllers/AdminUserController.cs @@ -1,5 +1,7 @@ using AsbCloudApp.Data; +using AsbCloudApp.Repositories; using AsbCloudApp.Services; +using AsbCloudInfrastructure.Repository; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -13,7 +15,7 @@ namespace AsbCloudWebApi.Controllers [Authorize] public class AdminUserController : CrudController> { - public AdminUserController(IUserService service) + public AdminUserController(IUserRepository service) : base(service) { } diff --git a/AsbCloudWebApi/Controllers/AdminUserRoleController.cs b/AsbCloudWebApi/Controllers/AdminUserRoleController.cs index 5625f0ae..d3ca48ba 100644 --- a/AsbCloudWebApi/Controllers/AdminUserRoleController.cs +++ b/AsbCloudWebApi/Controllers/AdminUserRoleController.cs @@ -1,4 +1,5 @@ using AsbCloudApp.Data; +using AsbCloudApp.Repositories; using AsbCloudApp.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -12,9 +13,9 @@ namespace AsbCloudWebApi.Controllers [Route("api/admin/role")] [ApiController] [Authorize] - public class AdminUserRoleController : CrudController + public class AdminUserRoleController : CrudController { - public AdminUserRoleController(IUserRoleService service) + public AdminUserRoleController(IUserRoleRepository service) : base(service) { UpdateForbidAsync = async (dto, token) => diff --git a/AsbCloudWebApi/Controllers/AuthController.cs b/AsbCloudWebApi/Controllers/AuthController.cs index aeec1f81..b2702f81 100644 --- a/AsbCloudWebApi/Controllers/AuthController.cs +++ b/AsbCloudWebApi/Controllers/AuthController.cs @@ -1,5 +1,7 @@ using AsbCloudApp.Data; +using AsbCloudApp.Repositories; using AsbCloudApp.Services; +using AsbCloudInfrastructure.Repository; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Swashbuckle.AspNetCore.Annotations; @@ -16,12 +18,12 @@ namespace AsbCloudWebApi.Controllers public class AuthController : ControllerBase { private readonly IAuthService authService; - private readonly IUserService userService; + private readonly IUserRepository userRepository; - public AuthController(IAuthService authService, IUserService userService) + public AuthController(IAuthService authService, IUserRepository userRepository) { this.authService = authService; - this.userService = userService; + this.userRepository = userRepository; } /// @@ -97,7 +99,7 @@ namespace AsbCloudWebApi.Controllers if (editorUserId is null) return Forbid(); - if (!((editorUserId == idUser) || userService.HasPermission((int)editorUserId, "Auth.edit"))) + if (!((editorUserId == idUser) || userRepository.HasPermission((int)editorUserId, "Auth.edit"))) return Forbid(); var code = authService.ChangePassword(idUser, newPassword); diff --git a/AsbCloudWebApi/Controllers/FileController.cs b/AsbCloudWebApi/Controllers/FileController.cs index d4dfad33..1340c55a 100644 --- a/AsbCloudWebApi/Controllers/FileController.cs +++ b/AsbCloudWebApi/Controllers/FileController.cs @@ -8,6 +8,8 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using AsbCloudDb.Model; +using AsbCloudInfrastructure.Repository; +using AsbCloudApp.Repositories; namespace AsbCloudWebApi.Controllers { @@ -42,7 +44,7 @@ namespace AsbCloudWebApi.Controllers [Permission] [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] public async Task SaveFilesAsync(int idWell, int idCategory, - [FromForm] IFormFileCollection files, [FromServices] IUserService userService, CancellationToken token = default) + [FromForm] IFormFileCollection files, [FromServices] IUserRepository userRepository, CancellationToken token = default) { int? idCompany = User.GetCompanyId(); int? idUser = User.GetUserId(); @@ -54,7 +56,7 @@ namespace AsbCloudWebApi.Controllers idWell, token).ConfigureAwait(false)) return Forbid(); - if (!userService.HasPermission((int)idUser, $"File.edit{idCategory}")) + if (!userRepository.HasPermission((int)idUser, $"File.edit{idCategory}")) return Forbid(); foreach (var file in files) @@ -140,7 +142,7 @@ namespace AsbCloudWebApi.Controllers [Permission] [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] public async Task DeleteAsync(int idWell, int idFile, - [FromServices] IUserService userService, + [FromServices] IUserRepository userRepository, CancellationToken token = default) { int? idUser = User.GetUserId(); @@ -156,7 +158,7 @@ namespace AsbCloudWebApi.Controllers if (fileInfo is null) return NotFound(idFile); - if (!userService.HasPermission((int)idUser, $"File.edit{fileInfo?.IdCategory}")) + if (!userRepository.HasPermission((int)idUser, $"File.edit{fileInfo?.IdCategory}")) return Forbid(); var result = await fileService.MarkAsDeletedAsync(idFile, token); diff --git a/AsbCloudWebApi/Controllers/Subsystems/SubsystemOperationTimeController.cs b/AsbCloudWebApi/Controllers/Subsystems/SubsystemOperationTimeController.cs index d86a411f..e2b1a945 100644 --- a/AsbCloudWebApi/Controllers/Subsystems/SubsystemOperationTimeController.cs +++ b/AsbCloudWebApi/Controllers/Subsystems/SubsystemOperationTimeController.cs @@ -46,6 +46,24 @@ namespace AsbCloudWebApi.Controllers.Subsystems return Ok(subsystemResult); } + /// + /// получить статистику по активным скважинам + /// + /// Больше или равно дате + /// Меньше или равно дате + /// Токен + /// + [HttpGet("statByActiveWell")] + [ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)] + public async Task GetStatByWellAsync(DateTime? GtDate, DateTime? LtDate, CancellationToken token = default) + { + var idCompany = User.GetCompanyId(); + if (!idCompany.HasValue) + return Forbid(); + var subsystemResult = await subsystemOperationTimeService.GetStatByActiveWells(idCompany.Value, GtDate, LtDate, token); + return Ok(subsystemResult); + } + /// /// получить список подсистем общий. /// diff --git a/AsbCloudWebApi/Middlewares/PermissionsMiddlware.cs b/AsbCloudWebApi/Middlewares/PermissionsMiddlware.cs index 00f7dd08..a6b2c07a 100644 --- a/AsbCloudWebApi/Middlewares/PermissionsMiddlware.cs +++ b/AsbCloudWebApi/Middlewares/PermissionsMiddlware.cs @@ -1,4 +1,5 @@ -using AsbCloudApp.Services; +using AsbCloudApp.Repositories; +using AsbCloudApp.Services; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -69,8 +70,8 @@ namespace AsbCloudWebApi.Middlewares PermissionAttribute.Registered.Add(permissionName); } - var userService = context.RequestServices.GetRequiredService(); - isAuthorized = userService.HasPermission(idUser!.Value, permissionName); + var userService = context.RequestServices.GetRequiredService(); + isAuthorized = userService.HasPermission(idUser.Value, permissionName); } if (isAuthorized)