forked from ddrilling/AsbCloudServer
Merge branch 'dev' into feature/TelemetryDataCache
This commit is contained in:
commit
95cf8dbd07
32
AsbCloudApp/Data/Subsystems/SubsystemActiveWellStatDto.cs
Normal file
32
AsbCloudApp/Data/Subsystems/SubsystemActiveWellStatDto.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System.Collections.Generic;
|
||||
namespace AsbCloudApp.Data.Subsystems
|
||||
{
|
||||
#nullable enable
|
||||
/// <summary>
|
||||
/// Статистика наработки подсистем по активным скважинам
|
||||
/// </summary>
|
||||
public class SubsystemActiveWellStatDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Активная скважина
|
||||
/// </summary>
|
||||
public WellDto Well { get; set; }
|
||||
/// <summary>
|
||||
/// Наработки подсистемы АКБ
|
||||
/// </summary>
|
||||
public SubsystemStatDto? SubsystemAKB { get; set; }
|
||||
/// <summary>
|
||||
/// Наработки подсистемы МСЕ
|
||||
/// </summary>
|
||||
public SubsystemStatDto? SubsystemMSE { get; set; }
|
||||
/// <summary>
|
||||
/// Наработки подсистемы СПИН
|
||||
/// </summary>
|
||||
public SubsystemStatDto? SubsystemSpinMaster { get; set; }
|
||||
/// <summary>
|
||||
/// Наработки подсистемы ТОРК
|
||||
/// </summary>
|
||||
public SubsystemStatDto? SubsystemTorqueMaster { get; set; }
|
||||
}
|
||||
#nullable disable
|
||||
}
|
@ -58,5 +58,25 @@
|
||||
/// DTO компании
|
||||
/// </summary>
|
||||
public CompanyDto Company { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Получение отображаемого имени
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,14 @@
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Services;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AsbCloudApp.Services
|
||||
namespace AsbCloudApp.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// Сервис пользователей
|
||||
/// Репозиторий пользователей
|
||||
/// </summary>
|
||||
public interface IUserService : ICrudService<UserExtendedDto>
|
||||
public interface IUserRepository : ICrudService<UserExtendedDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// Сервис ролей
|
||||
/// </summary>
|
||||
IUserRoleService RoleService { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Получить список всех прав пользователя (включая наследование групп)
|
||||
/// </summary>
|
@ -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
|
||||
/// <summary>
|
||||
/// Репозиторий ролей пользователя
|
||||
/// Разрешения на доступ к данным
|
||||
/// </summary>
|
||||
public interface IUserRoleService : ICrudService<UserRoleDto>
|
||||
public interface IUserRoleRepository : ICrudService<UserRoleDto>
|
||||
{
|
||||
// todo: переименовать
|
||||
/// <summary>
|
||||
/// получить dto по названиям
|
||||
/// </summary>
|
||||
@ -35,4 +36,5 @@ namespace AsbCloudApp.Services
|
||||
/// <returns></returns>
|
||||
bool HasPermission(IEnumerable<int> rolesIds, string permissionName);
|
||||
}
|
||||
}
|
||||
#nullable disable
|
||||
}
|
@ -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
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<DetectedOperationDto>?> GetOperationsAsync(DetectedOperationRequest request, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить интервалы глубин по всем скважинам
|
||||
/// </summary>
|
||||
/// <param name="telemetryIds">список ИД телеметрий активных скважин</param>
|
||||
/// <param name="gtDate"></param>
|
||||
/// <param name="ltDate"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns>кортеж - ид телеметрии, интервалы глубины забоя (ротор,слайд) </returns>
|
||||
Task<IEnumerable<(int idTelemetry,double depthIntervalRotor, double depthIntervalSlide)>> GetDepthIntervalAllOperationsAsync(IEnumerable<int?> telemetryIds,DateTimeOffset gtDate, DateTimeOffset ltDate, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Удалить операции
|
||||
/// </summary>
|
||||
|
@ -50,7 +50,8 @@ namespace AsbCloudApp.Services
|
||||
/// <summary>
|
||||
/// Сохранение файла
|
||||
/// </summary>
|
||||
/// <param name="idDto"></param>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="idCategory"></param>
|
||||
/// <param name="idUser"></param>
|
||||
/// <param name="fileStream"></param>
|
||||
/// <param name="fileName"></param>
|
||||
|
@ -46,7 +46,16 @@ namespace AsbCloudApp.Services.Subsystems
|
||||
/// <param name="request"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<DatesRangeDto?> GetDateRangeOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token);
|
||||
Task<DatesRangeDto?> GetDateRangeOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token);
|
||||
/// <summary>
|
||||
/// Получение статистики по наработке подсистем по активным скважинам
|
||||
/// </summary>
|
||||
/// <param name="idCompany"></param>
|
||||
/// <param name="gtDate"></param>
|
||||
/// <param name="ltDate"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatByActiveWells(int idCompany, DateTime? gtDate, DateTime? ltDate, CancellationToken token);
|
||||
}
|
||||
#nullable disable
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ namespace AsbCloudDb.Model
|
||||
DbSet<RelationUserDrillingProgramPart> RelationDrillingProgramPartUsers { get; }
|
||||
DbSet<RelationUserRolePermission> RelationUserRolePermissions { get; }
|
||||
DbSet<RelationUserUserRole> RelationUserUserRoles { get; }
|
||||
DbSet<RelationUserRoleUserRole> RelationUserRoleUserRoles { get; }
|
||||
DbSet<ReportProperty> ReportProperties { get; }
|
||||
DbSet<Subsystem> Subsystems { get; }
|
||||
DbSet<SubsystemOperationTime> SubsystemOperationTimes { get; }
|
||||
|
@ -9,6 +9,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="CommonLibs\logo_720x404.png" />
|
||||
<None Remove="CommonLibs\Readme.md" />
|
||||
<None Remove="Services\DailyReport\DailyReportTemplate.xlsx" />
|
||||
<None Remove="Services\WellOperationService\ScheduleReportTemplate.xlsx" />
|
||||
<None Remove="Services\WellOperationService\WellOperationImportTemplate.xlsx" />
|
||||
@ -16,7 +18,10 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Res\logo_32.png">
|
||||
<Content Include="CommonLibs\logo_720x404.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="CommonLibs\Readme.md">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
@ -43,7 +48,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="CommonLibs\" />
|
||||
<Folder Include="Services\DailyReport\DailyReportBlocks\" />
|
||||
</ItemGroup>
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
2
AsbCloudInfrastructure/CommonLibs/Readme.md
Normal file
2
AsbCloudInfrastructure/CommonLibs/Readme.md
Normal file
@ -0,0 +1,2 @@
|
||||
# Библиотека формирования рапортов в формате PDF.
|
||||
Для брендирования положить рядом с библиотекой файл logo_720x404.png
|
BIN
AsbCloudInfrastructure/CommonLibs/logo_720x404.png
Normal file
BIN
AsbCloudInfrastructure/CommonLibs/logo_720x404.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
@ -120,16 +120,12 @@ namespace AsbCloudInfrastructure
|
||||
services.AddTransient<ITelemetryService, TelemetryService>();
|
||||
services.AddTransient<ITelemetryUserService, TelemetryUserService>();
|
||||
services.AddTransient<ITimezoneService, TimezoneService>();
|
||||
services.AddTransient<IUserService, UserService>();
|
||||
services.AddTransient<IUserRoleService, UserRoleService>();
|
||||
services.AddTransient<IWellService, WellService>();
|
||||
services.AddTransient<IWellOperationImportService, WellOperationImportService>();
|
||||
services.AddTransient<IWellOperationService, WellOperationService>();
|
||||
services.AddTransient<IScheduleReportService, ScheduleReportService>();
|
||||
services.AddTransient<IDailyReportService, DailyReportService>();
|
||||
services.AddTransient<IDetectedOperationService, DetectedOperationService>();
|
||||
//services.AddTransient<IDrillerService, DrillerService>();
|
||||
|
||||
services.AddTransient<ISubsystemOperationTimeService, SubsystemOperationTimeService>();
|
||||
services.AddTransient<IScheduleRepository, ScheduleRepository>();
|
||||
services.AddTransient<IRepositoryWellRelated<OperationValueDto>, CrudWellRelatedServiceBase<OperationValueDto, OperationValue>>();
|
||||
@ -165,6 +161,8 @@ namespace AsbCloudInfrastructure
|
||||
services.AddTransient<IFileRepository, FileRepository>();
|
||||
services.AddTransient<IFileStorageRepository, FileStorageRepository>();
|
||||
services.AddTransient<IWellCompositeRepository, WellCompositeRepository>();
|
||||
services.AddTransient<IUserRoleRepository, UserRoleRepository>();
|
||||
services.AddTransient<IUserRepository, UserRepository>();
|
||||
// Subsystem service
|
||||
services.AddTransient<ICrudService<SubsystemDto>, CrudCacheServiceBase<SubsystemDto, Subsystem>>();
|
||||
services.AddTransient<ISubsystemService, SubsystemService>();
|
||||
|
@ -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<TEntity, TModel> : IEnumerable<TModel>
|
||||
{
|
||||
private struct ConvertedData
|
||||
{
|
||||
public TEntity? Entity;
|
||||
public TModel? Model;
|
||||
}
|
||||
|
||||
ConvertedData[] data;
|
||||
public Func<TEntity, TModel> convert { get; }
|
||||
|
||||
public YieldConvertedData(TEntity[] entities, Func<TEntity, TModel> convert)
|
||||
{
|
||||
data = (entities.Select(x => new ConvertedData {
|
||||
Entity = x,
|
||||
Model = default }))
|
||||
.ToArray();
|
||||
this.convert = convert;
|
||||
}
|
||||
|
||||
class YieldConvertedDataEnumerator : IEnumerator<TModel>
|
||||
{
|
||||
private readonly ConvertedData[] data;
|
||||
private readonly Func<TEntity, TModel> convert;
|
||||
private int position = -1;
|
||||
|
||||
public YieldConvertedDataEnumerator(ConvertedData[] data, Func<TEntity, TModel> 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<TModel> 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<TEntity, TModel>(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<IEnumerable> valueFactory, TimeSpan obsolete)
|
||||
private static CacheItem GetOrAddCache(string tag, Func<object[]> 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<CacheItem> GetOrAddCacheAsync(string tag, Func<CancellationToken, Task<IEnumerable>> valueFactoryAsync, TimeSpan obsolete, CancellationToken token)
|
||||
private static async Task<CacheItem> GetOrAddCacheAsync(string tag, Func<CancellationToken, Task<object[]>> 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<TEntity> FromCache<TEntity>(this IQueryable<TEntity> 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<TEntity>();
|
||||
}
|
||||
@ -282,7 +344,7 @@ namespace AsbCloudInfrastructure.EfCache
|
||||
public static IEnumerable<TModel> FromCache<TEntity, TModel>(this IQueryable<TEntity> query, string tag, TimeSpan obsolescence, Func<TEntity, TModel> 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<IEnumerable<TEntity>> FromCacheAsync<TEntity>(this IQueryable<TEntity> query, string tag, TimeSpan obsolescence, CancellationToken token = default)
|
||||
where TEntity : class
|
||||
{
|
||||
async Task<IEnumerable> factory(CancellationToken token)
|
||||
=> await query.AsNoTracking().ToListAsync(token);
|
||||
async Task<object[]> factory(CancellationToken token)
|
||||
=> await query.AsNoTracking().ToArrayAsync(token);
|
||||
var cache = await GetOrAddCacheAsync(tag, factory, obsolescence, token);
|
||||
return cache.GetData<TEntity>();
|
||||
}
|
||||
@ -327,8 +389,8 @@ namespace AsbCloudInfrastructure.EfCache
|
||||
public static async Task<IEnumerable<TModel>> FromCacheAsync<TEntity, TModel>(this IQueryable<TEntity> query, string tag, TimeSpan obsolescence, Func<TEntity, TModel> convert, CancellationToken token = default)
|
||||
where TEntity : class
|
||||
{
|
||||
async Task<IEnumerable> factory(CancellationToken token)
|
||||
=> await query.AsNoTracking().ToListAsync(token);
|
||||
async Task<object[]> factory(CancellationToken token)
|
||||
=> await query.AsNoTracking().ToArrayAsync(token);
|
||||
var cache = await GetOrAddCacheAsync(tag, factory, obsolescence, token);
|
||||
return cache.GetData(convert);
|
||||
}
|
||||
|
228
AsbCloudInfrastructure/Repository/UserRepository.cs
Normal file
228
AsbCloudInfrastructure/Repository/UserRepository.cs
Normal file
@ -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<UserExtendedDto, User>
|
||||
.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<int> 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<int> InsertRangeAsync(IEnumerable<UserExtendedDto> newItems, CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<UserExtendedDto>> GetAllAsync(CancellationToken token = default)
|
||||
{
|
||||
var dtos = (await GetCacheUserAsync(token)).ToList();
|
||||
if (dtos is null)
|
||||
return Enumerable.Empty<UserExtendedDto>();
|
||||
|
||||
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<UserExtendedDto?> 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<int> 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<int> 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<UserRoleDto> 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<PermissionDto> GetNestedPermissions(int idUser)
|
||||
{
|
||||
var roles = GetRolesByIdUser(idUser, 7);
|
||||
if (roles is null)
|
||||
return Enumerable.Empty<PermissionDto>();
|
||||
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<string> 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<IEnumerable<UserExtendedDto>> GetCacheUserAsync(CancellationToken token)
|
||||
=> dbContext.Users
|
||||
.Include(r => r.Company)
|
||||
.Include(r => r.RelationUsersUserRoles)
|
||||
.FromCacheAsync(userCacheTag, cacheObsolence, Convert, token);
|
||||
private IEnumerable<UserExtendedDto> GetCacheUser()
|
||||
=> dbContext.Users
|
||||
.Include(r => r.Company)
|
||||
.Include(r => r.RelationUsersUserRoles)
|
||||
.FromCache(userCacheTag, cacheObsolence, Convert);
|
||||
private void DropCacheUsers()
|
||||
=> dbContext.Users.DropCache(userCacheTag);
|
||||
|
||||
private Task<IEnumerable<RelationUserUserRole>> GetCacheRelationUserUserRoleAsync(CancellationToken token)
|
||||
=> dbContext.RelationUserUserRoles
|
||||
.Include(r => r.UserRole)
|
||||
.Include(r => r.User)
|
||||
.FromCacheAsync(relationUserUserRoleCacheTag, cacheObsolence, token);
|
||||
private IEnumerable<RelationUserUserRole> 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<UserRoleDto> 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<User>(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<UserExtendedDto>();
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
#nullable disable
|
||||
}
|
293
AsbCloudInfrastructure/Repository/UserRoleRepository.cs
Normal file
293
AsbCloudInfrastructure/Repository/UserRoleRepository.cs
Normal file
@ -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<int> InsertAsync(UserRoleDto dto, CancellationToken token)
|
||||
{
|
||||
var entity = dto.Adapt<UserRole>();
|
||||
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<int> InsertRangeAsync(IEnumerable<UserRoleDto> newItems, CancellationToken token)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<UserRoleDto>> 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<UserRoleDto?> 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<IEnumerable<UserRoleDto>> GetByNamesAsync(IEnumerable<string> names, CancellationToken token)
|
||||
{
|
||||
if (names?.Any() != true)
|
||||
return Enumerable.Empty<UserRoleDto>();
|
||||
|
||||
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<int> 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<UserRoleDto> GetNestedById(int id, int recursionLevel = 7)
|
||||
{
|
||||
var role = GetCacheUserRole()
|
||||
.FirstOrDefault(r => r.Id == id);
|
||||
if (role is null)
|
||||
return Enumerable.Empty<UserRoleDto>();
|
||||
|
||||
var roles = new SortedSet<UserRoleDto>(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<int> 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<int> 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<IEnumerable<UserRole>> GetCacheUserRoleAsync(CancellationToken token)
|
||||
=> dbContext.UserRoles
|
||||
.Include(r => r.RelationUserRolePermissions)
|
||||
.Include(r => r.RelationUserRoleUserRoles)
|
||||
.Include(r => r.RelationUsersUserRoles)
|
||||
.FromCacheAsync(userRoleCacheTag, relationCacheObsolence, token);
|
||||
private IEnumerable<UserRole> 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<IEnumerable<RelationUserRoleUserRole>> 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<IEnumerable<RelationUserRolePermission>> GetCacheRelationUserRolePermissionsAsync(CancellationToken token)
|
||||
=> dbContext.RelationUserRolePermissions
|
||||
.Include(r => r.UserRole)
|
||||
.Include(r => r.Permission)
|
||||
.FromCacheAsync(relationUserRolePermissionsCacheTag, relationCacheObsolence, token);
|
||||
private IEnumerable<RelationUserRolePermission> 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<UserRoleDto>();
|
||||
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<PermissionDto>();
|
||||
return dto;
|
||||
}
|
||||
|
||||
private static UserRole Convert(UserRoleDto dto)
|
||||
{
|
||||
var entity = dto.Adapt<UserRole>();
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
#nullable disable
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 4.7 KiB |
@ -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<UserTokenDto>();
|
||||
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));
|
||||
|
@ -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<IEnumerable<(int idTelemetry, double depthIntervalRotor, double depthIntervalSlide)>> GetDepthIntervalAllOperationsAsync(IEnumerable<int?> telemetryIds, DateTimeOffset gtDate, DateTimeOffset ltDate, CancellationToken token)
|
||||
{
|
||||
var query = db.Set<DetectedOperation>()
|
||||
.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<DetectedOperationDrillersStatDto> GetOperationsDrillersStat(IEnumerable<DetectedOperationDto> operations)
|
||||
@ -301,6 +325,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
|
||||
var exportService = new DetectedOperationExportService(db, wellService);
|
||||
return exportService.ExportAsync(idsWells, token);
|
||||
}
|
||||
|
||||
}
|
||||
#nullable disable
|
||||
}
|
||||
|
@ -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<FileInfoDto>(),
|
||||
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<int> 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));
|
||||
|
||||
|
@ -15,7 +15,7 @@ namespace AsbCloudInfrastructure.Services
|
||||
: base(context)
|
||||
{
|
||||
}
|
||||
|
||||
//TODO: Перенести в сервис "дело скважины"
|
||||
public async Task<IEnumerable<FileCategoryDto>> GetWellCaseCategoriesAsync(CancellationToken token)
|
||||
{
|
||||
var cache = await GetCacheAsync(token)
|
||||
|
@ -25,6 +25,10 @@ namespace AsbCloudInfrastructure.Services.Subsystems
|
||||
private readonly IWellService wellService;
|
||||
private readonly ICrudService<SubsystemDto> 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<SubsystemDto> 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<SubsystemStatDto> CalcStat(IEnumerable<SubsystemOperationTimeDto> dtos, (double depthIntervalRotor, double depthIntervalSlide) depthInterval, SubsystemOperationTimeRequest request, CancellationToken token)
|
||||
private IEnumerable<SubsystemStatDto> CalcStat(IEnumerable<SubsystemOperationTimeDto> 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<DetectedOperationDto> detectedOperations)
|
||||
{
|
||||
|
||||
private static (double depthIntervalRotor, double depthIntervalSlide) GetDepthInterval (IEnumerable<DetectedOperationDto> 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<IEnumerable<WellDto>> GetActiveWellsByCompany(int idCompany, CancellationToken token)
|
||||
{
|
||||
var listWell = await wellService.GetWellsByCompanyAsync(idCompany, token);
|
||||
var active = listWell.Where(w => w.IdState == 1);
|
||||
return active;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatByActiveWells(int idCompany, DateTime? gtDate, DateTime? ltDate, CancellationToken token)
|
||||
{
|
||||
var wells = await GetActiveWellsByCompany(idCompany, token);
|
||||
if (!wells.Any())
|
||||
return Enumerable.Empty<SubsystemActiveWellStatDto>();
|
||||
|
||||
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<SubsystemStatDto>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<DatesRangeDto?> 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<SubsystemOperationTimeDto>();
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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<UserRole> cacheUserRoles;
|
||||
private readonly CacheTable<RelationUserRolePermission> cacheRelationUserRolePermissions;
|
||||
private readonly CacheTable<RelationUserRoleUserRole> cacheRelationUserRoleUserRole;
|
||||
|
||||
public ISet<string> Includes { get; } = new SortedSet<string>();
|
||||
|
||||
public UserRoleService(IAsbCloudDbContext context, CacheDb cacheDb)
|
||||
{
|
||||
cacheUserRoles = cacheDb.GetCachedTable<UserRole>((AsbCloudDbContext)context, nameof(UserRole.RelationUserRolePermissions), nameof(UserRole.RelationUserRoleUserRoles));
|
||||
cacheRelationUserRolePermissions = cacheDb.GetCachedTable<RelationUserRolePermission>((AsbCloudDbContext)context, nameof(RelationUserRolePermission.Permission));
|
||||
cacheRelationUserRoleUserRole = cacheDb.GetCachedTable<RelationUserRoleUserRole>((AsbCloudDbContext)context, nameof(RelationUserRoleUserRole.IncludeRole), nameof(RelationUserRoleUserRole.Role));
|
||||
}
|
||||
|
||||
public async Task<int> InsertAsync(UserRoleDto dto, CancellationToken token = default)
|
||||
{
|
||||
var entity = dto.Adapt<UserRole>();
|
||||
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<int> InsertRangeAsync(IEnumerable<UserRoleDto> dtos, CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
//var entities = dtos.Adapt<UserRole>();
|
||||
//return await cacheUserRoles.InsertAsync(entities, token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<UserRoleDto>> 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<UserRoleDto> 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<IEnumerable<UserRoleDto>> GetByNamesAsync(IEnumerable<string> 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<int> 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<UserRoleDto> 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<UserRoleDto>(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<int> DeleteAsync(int id, CancellationToken token = default)
|
||||
=> cacheUserRoles.RemoveAsync(r => r.Id == id, token);
|
||||
|
||||
public Task<int> DeleteAsync(IEnumerable<int> ids, CancellationToken token = default)
|
||||
=> cacheUserRoles.RemoveAsync(r => ids.Contains(r.Id), token);
|
||||
|
||||
public bool HasPermission(IEnumerable<int> 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<UserRole>();
|
||||
return entity;
|
||||
}
|
||||
|
||||
private UserRoleDto Convert(UserRole entity)
|
||||
{
|
||||
var dto = entity.Adapt<UserRoleDto>();
|
||||
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<PermissionDto>();
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
}
|
@ -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<User> cacheUsers;
|
||||
private readonly CacheTable<RelationUserUserRole> cacheRelationUserToRoles;
|
||||
public ISet<string> Includes { get; } = new SortedSet<string>();
|
||||
public IUserRoleService RoleService { get; }
|
||||
|
||||
private static readonly TypeAdapterConfig userTypeAdapterConfig = TypeAdapterConfig<UserExtendedDto, User>
|
||||
.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<User>(
|
||||
db,
|
||||
new[] {
|
||||
nameof(User.RelationUsersUserRoles),
|
||||
nameof(User.Company),
|
||||
});
|
||||
cacheRelationUserToRoles = cacheDb.GetCachedTable<RelationUserUserRole>(
|
||||
db,
|
||||
new[] {
|
||||
nameof(RelationUserUserRole.User),
|
||||
nameof(RelationUserUserRole.UserRole),
|
||||
});
|
||||
RoleService = roleService;
|
||||
}
|
||||
|
||||
public async Task<int> 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<int> InsertRangeAsync(IEnumerable<UserExtendedDto> newItems, CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<UserExtendedDto>> 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<UserExtendedDto> 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<int> 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<int> DeleteAsync(int id, CancellationToken token = default)
|
||||
{
|
||||
if (id <= 1)
|
||||
return Task.FromResult(0);
|
||||
return cacheUsers.RemoveAsync(r => r.Id == id, token);
|
||||
}
|
||||
|
||||
public Task<int> DeleteAsync(IEnumerable<int> ids, CancellationToken token = default)
|
||||
{
|
||||
var filteredIds = ids.Where(i => i > 1).ToList();
|
||||
return cacheUsers.RemoveAsync(r => filteredIds.Contains(r.Id), token);
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetRolesNamesByIdUser(int idUser)
|
||||
=> GetRolesByIdUser(idUser)
|
||||
?.Select(r => r.Caption)
|
||||
.Distinct();
|
||||
|
||||
public IEnumerable<UserRoleDto> 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<PermissionDto> 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<UserRoleDto> 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<User>(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<UserExtendedDto>();
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
61
AsbCloudWebApi.Tests/CacheTests/CacheTest.cs
Normal file
61
AsbCloudWebApi.Tests/CacheTests/CacheTest.cs
Normal file
@ -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<User> UsersCache = new List<User> {
|
||||
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<UserExtendedDto>();
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
}
|
@ -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<FileService> fileServiceMock;
|
||||
private readonly Mock<IUserService> userServiceMock;
|
||||
private readonly Mock<IUserRepository> userRepositoryMock;
|
||||
private readonly Mock<IWellService> wellServiceMock;
|
||||
private readonly Mock<IConfiguration> configurationMock;
|
||||
private readonly Mock<IBackgroundWorkerService> backgroundWorkerMock;
|
||||
@ -98,7 +99,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests
|
||||
db.SaveChanges();
|
||||
|
||||
fileServiceMock = new Mock<FileService>();
|
||||
userServiceMock = new Mock<IUserService>();
|
||||
userRepositoryMock = new Mock<IUserRepository>();
|
||||
wellServiceMock = new Mock<IWellService>();
|
||||
configurationMock = new Mock<IConfiguration>();
|
||||
backgroundWorkerMock = new Mock<IBackgroundWorkerService>();
|
||||
@ -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<int>(), It.IsAny<CancellationToken>()))
|
||||
userRepositoryMock.Setup((s) => s.GetOrDefaultAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(publisher1.Adapt<UserExtendedDto>()));
|
||||
|
||||
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<int>(), It.IsAny<CancellationToken>()))
|
||||
userRepositoryMock.Setup((s) => s.GetOrDefaultAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(publisher1.Adapt<UserExtendedDto>()));
|
||||
|
||||
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,
|
||||
|
@ -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<UserRole> 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<RelationUserRoleUserRole> 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<Permission> 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<RelationUserRolePermission> 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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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<IUserRoleService> roleService;
|
||||
|
||||
public UserServiceTest()
|
||||
{
|
||||
context = TestHelpter.MakeTestContext();
|
||||
cacheDb = new CacheDb();
|
||||
roleService = new Mock<IUserRoleService>();
|
||||
|
||||
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<ArgumentException>(() => service.InsertAsync(dto, CancellationToken.None));
|
||||
}
|
||||
}
|
||||
}
|
@ -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<FileService> fileServiceMock;
|
||||
private readonly Mock<IUserService> userServiceMock;
|
||||
private readonly Mock<IUserRepository> userRepositoryMock;
|
||||
private readonly Mock<IWellService> wellServiceMock;
|
||||
private readonly Mock<IConfiguration> configurationMock;
|
||||
private readonly Mock<IEmailService> emailServiceMock;
|
||||
@ -45,8 +47,8 @@ namespace AsbCloudWebApi.Tests.ServicesTests
|
||||
context.SaveChanges();
|
||||
|
||||
fileServiceMock = new Mock<FileService>();
|
||||
userServiceMock = new Mock<IUserService>();
|
||||
userServiceMock.Setup(x => x.GetAllAsync(CancellationToken.None)).Returns(Task.Run(() => users.Select(x => (UserExtendedDto)x)));
|
||||
userRepositoryMock = new Mock<IUserRepository>();
|
||||
userRepositoryMock.Setup(x => x.GetAllAsync(CancellationToken.None)).Returns(Task.Run(() => users.Select(x => (UserExtendedDto)x)));
|
||||
|
||||
wellServiceMock = new Mock<IWellService>();
|
||||
configurationMock = new Mock<IConfiguration>();
|
||||
@ -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,
|
||||
|
@ -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<UserExtendedDto, ICrudService<UserExtendedDto>>
|
||||
{
|
||||
public AdminUserController(IUserService service)
|
||||
public AdminUserController(IUserRepository service)
|
||||
: base(service)
|
||||
{ }
|
||||
|
||||
|
@ -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<UserRoleDto, IUserRoleService>
|
||||
public class AdminUserRoleController : CrudController<UserRoleDto, IUserRoleRepository>
|
||||
{
|
||||
public AdminUserRoleController(IUserRoleService service)
|
||||
public AdminUserRoleController(IUserRoleRepository service)
|
||||
: base(service)
|
||||
{
|
||||
UpdateForbidAsync = async (dto, token) =>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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);
|
||||
|
@ -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<IActionResult> 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<IActionResult> 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);
|
||||
|
@ -46,6 +46,24 @@ namespace AsbCloudWebApi.Controllers.Subsystems
|
||||
return Ok(subsystemResult);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// получить статистику по активным скважинам
|
||||
/// </summary>
|
||||
/// <param name="GtDate"> Больше или равно дате </param>
|
||||
/// <param name="LtDate"> Меньше или равно дате </param>
|
||||
/// <param name="token"> Токен </param>
|
||||
/// <returns> </returns>
|
||||
[HttpGet("statByActiveWell")]
|
||||
[ProducesResponseType(typeof(IEnumerable<SubsystemActiveWellStatDto>), (int)System.Net.HttpStatusCode.OK)]
|
||||
public async Task<IActionResult> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// получить список подсистем общий.
|
||||
/// </summary>
|
||||
|
@ -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<IUserService>();
|
||||
isAuthorized = userService.HasPermission(idUser!.Value, permissionName);
|
||||
var userService = context.RequestServices.GetRequiredService<IUserRepository>();
|
||||
isAuthorized = userService.HasPermission(idUser.Value, permissionName);
|
||||
}
|
||||
|
||||
if (isAuthorized)
|
||||
|
Loading…
Reference in New Issue
Block a user