diff --git a/AsbCloudDb/Model/AsbCloudDbContext.cs b/AsbCloudDb/Model/AsbCloudDbContext.cs index afce3792..a43f55c1 100644 --- a/AsbCloudDb/Model/AsbCloudDbContext.cs +++ b/AsbCloudDb/Model/AsbCloudDbContext.cs @@ -361,6 +361,6 @@ namespace AsbCloudDb.Model { var sql = $"REFRESH MATERIALIZED VIEW {materializedViewName};"; return Database.ExecuteSqlRawAsync(sql, token); - } + } } } diff --git a/AsbCloudDb/Model/IAsbCloudDbContext.cs b/AsbCloudDb/Model/IAsbCloudDbContext.cs index 4eac95f9..446a7656 100644 --- a/AsbCloudDb/Model/IAsbCloudDbContext.cs +++ b/AsbCloudDb/Model/IAsbCloudDbContext.cs @@ -67,9 +67,7 @@ namespace AsbCloudDb.Model Task RefreshMaterializedViewAsync(CancellationToken token) where TEntity : class; int SaveChanges(); int SaveChanges(bool acceptAllChangesOnSuccess); - Task SaveChangesAsync(CancellationToken cancellationToken); - DbSet Set(string name) where TEntity : class; - DbSet Set() where TEntity : class; - + Task SaveChangesAsync(CancellationToken cancellationToken); + DbSet Set() where TEntity : class; } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Repository/UserRepository.cs b/AsbCloudInfrastructure/Repository/UserRepository.cs index efc0db16..8ddbc5f3 100644 --- a/AsbCloudInfrastructure/Repository/UserRepository.cs +++ b/AsbCloudInfrastructure/Repository/UserRepository.cs @@ -1,9 +1,7 @@ using AsbCloudApp.Data; using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; -using AsbCloudDb; using AsbCloudDb.Model; -using AsbCloudInfrastructure.EfCache; using Mapster; using Microsoft.EntityFrameworkCore; using System; @@ -11,6 +9,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Memory; namespace AsbCloudInfrastructure.Repository { @@ -29,10 +28,12 @@ namespace AsbCloudInfrastructure.Repository dst => dst.Files, dst => dst.RelationUsersUserRoles) .Config; - - public UserRepository(IAsbCloudDbContext dbContext, IUserRoleRepository userRoleRepository) { + private readonly IMemoryCache memoryCache; + public UserRepository(IAsbCloudDbContext dbContext, IUserRoleRepository userRoleRepository, IMemoryCache memoryCache) + { this.dbContext = dbContext; this.userRoleRepository = userRoleRepository; + this.memoryCache = memoryCache; } public async Task InsertAsync(UserExtendedDto dto, CancellationToken token) @@ -58,31 +59,36 @@ namespace AsbCloudInfrastructure.Repository public async Task> GetAllAsync(CancellationToken token) { - var dtos = (await GetCacheUserAsync(token)).ToList(); - if (dtos is null) + var users = await GetCacheUserAsync(token); + if (users is null) return Enumerable.Empty(); - - for (var i = 0; i < dtos.Count; i++) - dtos[i].RoleNames = GetRolesNamesByIdUser(dtos[i].Id); + var dtos = new List(); + foreach(var user in users) + { + var dto = Convert(user); + dto.RoleNames = GetRolesNamesByIdUser(user.Id); + dtos.Add(dto); + }; return dtos; } + public UserExtendedDto? GetOrDefault(int id) { - var dto = GetCacheUser().FirstOrDefault(u => u.Id == id); - if (dto is null) + var user = GetCacheUser().FirstOrDefault(u => u.Id == id); + if (user is null) return null; - var entity = Convert(dto); + var dto = Convert(user); dto.RoleNames = GetRolesNamesByIdUser(dto.Id); return dto; } public async Task GetOrDefaultAsync(int id, CancellationToken token) - { - var dto = (await GetCacheUserAsync(token)).FirstOrDefault(u => u.Id == id); - if (dto is null) + { + var user = (await GetCacheUserAsync(token)).FirstOrDefault(u => u.Id == id); + if (user is null) return null; - + var dto = Convert(user); dto.RoleNames = GetRolesNamesByIdUser(dto.Id); return dto; } @@ -90,11 +96,13 @@ namespace AsbCloudInfrastructure.Repository public async Task UpdateAsync(UserExtendedDto dto, CancellationToken token) { if (dto.Id <= 1) - throw new ArgumentInvalidException($"Invalid id {dto.Id}. You can't edit this user.", nameof(dto)); + 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; + throw new ArgumentInvalidException + ($"Invalid id {dto.Id}. You can't edit this user.", nameof(dto)); if (oldUser.Login != dto.Login) await AssertLoginIsBusyAsync(dto.Login, token); @@ -102,25 +110,43 @@ namespace AsbCloudInfrastructure.Repository 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); + var entity = dbContext.Users.FirstOrDefault(u => u.Id == dto.Id); + if (entity is null) + throw new ArgumentInvalidException + ($"Invalid id {dto.Id}. You can't edit this user.", nameof(dto)); + entity.Id = dto.Id; + entity.Login = dto.Login; + entity.Name = dto.Name; + entity.Email = dto.Email; + entity.Phone = dto.Phone; + entity.Surname = dto.Surname; + entity.Patronymic = dto.Patronymic; + entity.Position = dto.Position; + entity.IdCompany = dto.IdCompany; + entity.IdState = dto.IdState; await dbContext.SaveChangesAsync(token); DropCacheUsers(); - return result.Entity.Id; + return entity.Id; } public async Task DeleteAsync(int id, CancellationToken token) { - 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; + var user = (await GetCacheUserAsync(token)).FirstOrDefault(u => u.Id == id); + if (user is null) + throw new ArgumentInvalidException + ($"Invalid id {id}. You can't edit this user.", nameof(id)); + var query = dbContext + .Users + .Where(u => u.Id == id); + if (query.Any()) + { + var result = dbContext.Users.Remove(query.First()); + await dbContext.SaveChangesAsync(token); + DropCacheUsers(); + return result.Entity.Id; + } + throw new ArgumentInvalidException + ($"Invalid id {id}. You can't edit this user.", nameof(id)); } public IEnumerable GetRolesByIdUser(int idUser, int nestedLevel = 0) @@ -156,9 +182,14 @@ namespace AsbCloudInfrastructure.Repository } private IEnumerable GetRolesNamesByIdUser(int idUser) - => GetRolesByIdUser(idUser, 7) - .Select(r => r.Caption) - .Distinct(); + { + var userRoles = GetRolesByIdUser(idUser, 7) + .Select(r => r.Caption) + .Distinct(); + if (userRoles.Any()) + return userRoles; + return Enumerable.Empty(); + } private async Task AssertLoginIsBusyAsync(string login, CancellationToken token) { @@ -167,63 +198,87 @@ namespace AsbCloudInfrastructure.Repository if (existingUserDto is not null) throw new ArgumentInvalidException($"Login {login} is busy by {existingUserDto.MakeDisplayName()}, id{existingUserDto.Id}", nameof(login)); + } + + + private IEnumerable GetCachRelationUserUserRoleCacheTag() + { + var cache = memoryCache.GetOrCreate(relationUserUserRoleCacheTag, cacheEntry => + { + cacheEntry.AbsoluteExpirationRelativeToNow = cacheObsolence; + cacheEntry.SlidingExpiration = cacheObsolence; + var query = dbContext.RelationUserUserRoles + .Include(r => r.UserRole) + .Include(r => r.User); + var entities = query.ToArray(); + return entities; + }); + return cache!; + } + private void DropCacheRelationUserUserRoleCacheTag() + { + memoryCache.Remove(relationUserUserRoleCacheTag); } - private Task> GetCacheUserAsync(CancellationToken token) - => dbContext.Users - .Include(r => r.Company) - .Include(r => r.RelationUsersUserRoles) - .FromCacheAsync(userCacheTag, cacheObsolence, Convert, token); - private IEnumerable GetCacheUser() - => dbContext.Users - .Include(r => r.Company) - .Include(r => r.RelationUsersUserRoles) - .FromCache(userCacheTag, cacheObsolence, Convert); - private void DropCacheUsers() - => dbContext.Users.DropCache(userCacheTag); - - private Task> GetCacheRelationUserUserRoleAsync(CancellationToken token) - => dbContext.RelationUserUserRoles - .Include(r => r.UserRole) - .Include(r => r.User) - .FromCacheAsync(relationUserUserRoleCacheTag, cacheObsolence, token); - private IEnumerable GetCachRelationUserUserRoleCacheTag() - => dbContext.RelationUserUserRoles - .Include(r => r.UserRole) - .Include(r => r.User) - .FromCache(relationUserUserRoleCacheTag, cacheObsolence); - private void DropCacheRelationUserUserRoleCacheTag() - => dbContext.RelationUserUserRoles.DropCache(relationUserUserRoleCacheTag); - - private async Task UpdateRolesCacheForUserAsync(int idUser, IEnumerable newRoles, CancellationToken token) + private async Task UpdateRolesCacheForUserAsync(int idUser, IEnumerable roleDtos, CancellationToken token) { - var relations = (await GetCacheRelationUserUserRoleAsync(token)).Where(r => r.IdUser == idUser); + var relations = dbContext.RelationUserUserRoles.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); - + var entityRoles = roleDtos.Select(role => new RelationUserUserRole + { + IdUser = idUser, + IdUserRole = role.Id + }); + dbContext.RelationUserUserRoles.AddRange(entityRoles); await dbContext.SaveChangesAsync(token); DropCacheRelationUserUserRoleCacheTag(); } - protected virtual User Convert(UserExtendedDto dto) + private void DropCacheUsers() + => memoryCache.Remove(userCacheTag); + + private IEnumerable GetCacheUser() + { + var cache = memoryCache.GetOrCreate(userCacheTag, cacheEntry => + { + cacheEntry.AbsoluteExpirationRelativeToNow = cacheObsolence; + cacheEntry.SlidingExpiration = cacheObsolence; + var query = dbContext.Users + .Include(r => r.Company) + .Include(r => r.RelationUsersUserRoles); + var entities = query.ToArray(); + return entities; + }); + return cache!; + } + + private Task> GetCacheUserAsync(CancellationToken token) + { + var cache = memoryCache.GetOrCreateAsync(userCacheTag, async (cacheEntry) => + { + cacheEntry.AbsoluteExpirationRelativeToNow = cacheObsolence; + cacheEntry.SlidingExpiration = cacheObsolence; + var query = dbContext.Users + .Include(r => r.Company) + .Include(r => r.RelationUsersUserRoles); + var entities = await query.ToArrayAsync(token); + return entities.AsEnumerable(); + }); + return cache!; + } + + protected User Convert(UserExtendedDto dto) { var entity = dto.Adapt(userTypeAdapterConfig); if (string.IsNullOrEmpty(entity.PasswordHash)) entity.PasswordHash = dbContext.Users.FirstOrDefault(u => u.Id == dto.Id)?.PasswordHash; return entity; } - protected virtual UserExtendedDto Convert(User entity) { var dto = entity.Adapt(); return dto; - } + } } #nullable disable } diff --git a/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapReportService.cs b/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapReportService.cs index da7f3f79..923a6bf5 100644 --- a/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapReportService.cs +++ b/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapReportService.cs @@ -13,9 +13,9 @@ namespace AsbCloudInfrastructure.Services.ProcessMap public class ProcessMapReportService : IProcessMapReportService { const int firstColumn = 2; - const int lastColumn = 27; + const int lastColumn = 61; - const int headerRowsCount = 3; + const int headerRowsCount = 8; private readonly IProcessMapService processMapService; @@ -68,7 +68,9 @@ namespace AsbCloudInfrastructure.Services.ProcessMap sheet.Range(row, firstColumn, row, lastColumn) .Merge() .FirstCell() - .SetVal(sectionName); + .SetVal(sectionName) + .Style + .Fill.SetBackgroundColor(XLColor.LightGray); row++; @@ -82,14 +84,17 @@ namespace AsbCloudInfrastructure.Services.ProcessMap private static int FillIntervalData(IXLWorksheet sheet, ProcessMapReportDto interval, int row) { - const int columnDepth = firstColumn; - const int columnDate = firstColumn + 1; - const int columnRopTime = firstColumn + 2; - const int columnMode = firstColumn + 3; + const int columnDepth = firstColumn + 1; + const int columnDate = firstColumn + 2; + const int columnRopTime = firstColumn + 3; + const int columnMode = firstColumn + 4; int rowRotor = row; int rowSlide = row + 1; + sheet.Range(rowRotor, firstColumn, rowSlide, firstColumn) + .Merge(); + sheet.Range(rowRotor, columnDepth, rowSlide, columnDepth) .Merge().FirstCell() .SetVal(interval.DepthStart, "0.0"); @@ -103,7 +108,7 @@ namespace AsbCloudInfrastructure.Services.ProcessMap .SetVal(interval.MechDrillingHours); row = FillIntervalModeData(sheet, "Ротор", interval.Rotor, columnMode, row); - row = FillIntervalModeData(sheet, "Слайд", interval.Rotor, columnMode, row); + row = FillIntervalModeData(sheet, "Слайд", interval.Slide, columnMode, row); return row; } @@ -115,8 +120,9 @@ namespace AsbCloudInfrastructure.Services.ProcessMap int columnLoad = columnPressure + 5; int columnTorque = columnLoad + 5; int columnSpeed = columnTorque + 5; - int columnUsage = columnSpeed + 4; - int columnRop = columnUsage + 1; + int columnUsagePlan = columnSpeed + 5; + int columnUsageFact = columnUsagePlan + 1; + int columnRop = columnUsageFact + 12; sheet.Cell(row, column) .SetVal(modeName); @@ -129,7 +135,10 @@ namespace AsbCloudInfrastructure.Services.ProcessMap FillIntervalModeDataParam(sheet, modeData.TopDriveTorque, columnTorque, row); FillIntervalModeDataSpeed(sheet, modeData.SpeedLimit, columnSpeed, row); - sheet.Cell(row, columnUsage) + sheet.Cell(row, columnUsagePlan) + .SetVal(100); + + sheet.Cell(row, columnUsageFact) .SetVal(modeData.Usage); sheet.Cell(row, columnRop) @@ -167,7 +176,8 @@ namespace AsbCloudInfrastructure.Services.ProcessMap const int columnOffsetSpPlan = 0; const int columnOffsetSpFact = 1; const int columnOffsetFact = 2; - const int columnOffsetPercent = 3; + const int columnOffsetLimit = 3; + const int columnOffsetPercent = 4; sheet.Cell(row, column + columnOffsetSpPlan) .SetVal(dataParam.SetpointPlan); @@ -178,6 +188,9 @@ namespace AsbCloudInfrastructure.Services.ProcessMap sheet.Cell(row, column + columnOffsetFact) .SetVal(dataParam.Fact); + sheet.Cell(row, column + columnOffsetLimit) + .SetVal(dataParam.Limit); + sheet.Cell(row, column + columnOffsetPercent) .SetVal(dataParam.PercDrillingSetpoint); } diff --git a/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapReportTemplate.xlsx b/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapReportTemplate.xlsx index 64b328cd..65b418cc 100644 Binary files a/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapReportTemplate.xlsx and b/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapReportTemplate.xlsx differ diff --git a/AsbCloudWebApi/Controllers/ProcessMapController.cs b/AsbCloudWebApi/Controllers/ProcessMapController.cs index 3a1430b9..2837af73 100644 --- a/AsbCloudWebApi/Controllers/ProcessMapController.cs +++ b/AsbCloudWebApi/Controllers/ProcessMapController.cs @@ -22,13 +22,20 @@ namespace AsbCloudWebApi.Controllers { private readonly ITelemetryService telemetryService; private readonly IProcessMapReportService processMapReportService; + private readonly IProcessMapService processMapService; - public ProcessMapController(IWellService wellService, IProcessMapRepository repository, IProcessMapReportService processMapReportService, + public ProcessMapController( + IWellService wellService, + IProcessMapRepository repository, + IProcessMapReportService processMapReportService, + IProcessMapService processMapService, ITelemetryService telemetryService) : base(wellService, repository) { this.telemetryService = telemetryService; this.processMapReportService = processMapReportService; + this.processMapService = processMapService; + } /// @@ -104,6 +111,21 @@ namespace AsbCloudWebApi.Controllers return NoContent(); } + /// + /// Выгрузка режимной карты по бурению скважины + /// + /// + /// + /// + [HttpGet] + [Route("getDrillProcessMap/{wellId}")] + [ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)] + public async Task GetDrillProcessMap(int wellId, CancellationToken token) + { + var data = await processMapService.GetProcessMapAsync(wellId, token); + return Ok(data); + } + /// /// Добавить запись ///