using AsbCloudApp.Data; using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudDb.Model; using AsbCloudInfrastructure.Repository; using Mapster; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services; public class WellService : CrudCacheRepositoryBase, IWellService { private readonly ITelemetryService telemetryService; private readonly ICrudRepository companyTypesService; private readonly ITimezoneService timezoneService; private readonly WellInfoService wellInfoService; private readonly IWellOperationRepository wellOperationRepository; public ITelemetryService TelemetryService => telemetryService; private static IQueryable MakeQueryWell(DbSet dbSet) => dbSet .Include(w => w.Cluster) .ThenInclude(c => c.Deposit) .Include(w => w.Telemetry) .Include(w => w.WellType) .Include(w => w.RelationCompaniesWells) .ThenInclude(r => r.Company) .AsNoTracking(); public WellService(IAsbCloudDbContext db, IMemoryCache memoryCache, ITelemetryService telemetryService, ITimezoneService timezoneService, WellInfoService wellInfoService, IWellOperationCategoryRepository wellOperationCategoryRepository) : base(db, memoryCache, MakeQueryWell) { this.telemetryService = telemetryService; this.timezoneService = timezoneService; this.wellInfoService = wellInfoService; wellOperationRepository = new WellOperationRepository(db, memoryCache, wellOperationCategoryRepository, this); companyTypesService = new CrudCacheRepositoryBase(dbContext, memoryCache); } private Task> GetCacheRelationCompanyWellAsync(CancellationToken token) { return memoryCache.GetOrCreateBasicAsync( dbContext.Set() .Include(r => r.Company) .Include(r => r.Well) , token); } private void DropCacheRelationCompanyWell() => memoryCache.DropBasic(); public DateTimeOffset GetLastTelemetryDate(int idWell) { var well = GetOrDefault(idWell); if (well?.IdTelemetry is null) return DateTimeOffset.MinValue; var datesRange = telemetryService.GetDatesRange(well.IdTelemetry.Value); return datesRange.To; } /// public async Task> GetWellTreeAsync(int idCompany, CancellationToken token) { var wells = await GetEntitiesAsync(new() { IdCompany = idCompany }, token); var groupedWells = wells .GroupBy(w => w.Cluster) .GroupBy(g => g.Key.Deposit); var depositTree = groupedWells.Select( gDeposit => new DepositBranchDto { Id = gDeposit.Key.Id, Caption = gDeposit.Key.Caption, Latitude = gDeposit.Key.Latitude, Longitude = gDeposit.Key.Longitude, Clusters = gDeposit.Select(gCluster => new ClusterBranchDto { Id = gCluster.Key.Id, Caption = gCluster.Key.Caption, Latitude = gCluster.Key.Latitude ?? gDeposit.Key.Latitude, Longitude = gCluster.Key.Longitude ?? gDeposit.Key.Longitude, Wells = gCluster.Select(well => { var dto = wellInfoService.FirstOrDefault(w => w.Id == well.Id && well.IdState == 1); dto ??= well.Adapt(); dto.Latitude ??= gCluster.Key.Latitude ?? gDeposit.Key.Latitude; dto.Longitude ??= gCluster.Key.Longitude ?? gDeposit.Key.Longitude; dto.Companies = well.RelationCompaniesWells.Select(r => Convert(r.Company)); return dto; }), }), }); return depositTree; } public async Task GetOrDefaultStatAsync(int idWell, CancellationToken token) { var well = await GetOrDefaultAsync(idWell, token); if (well is null) return null; var wellInfo = wellInfoService.FirstOrDefault(well => well.Id == idWell); if (wellInfo is null) return well.Adapt(); wellInfo.IdState = well.IdState; return wellInfo; } public async Task> GetAsync(WellRequest request, CancellationToken token) { var wells = await GetEntitiesAsync(request, token); var wellsDtos = wells.Select(Convert); return wellsDtos; } private async Task> GetEntitiesAsync(WellRequest request, CancellationToken token) { var wells = await GetCacheAsync(token); if (request.Ids?.Any() == true) wells = wells.Where(well => request.Ids.Contains(well.Id)); if (request.IdCompany.HasValue) wells = wells.Where(well => well.RelationCompaniesWells.Any(r => r.IdCompany == request.IdCompany.Value)); if (request.IdState.HasValue) wells = wells.Where(well => well.IdState == request.IdState.Value); return wells; } public override async Task InsertAsync(WellDto dto, CancellationToken token) { if (IsTelemetryAssignedToDifferentWell(dto)) throw new ArgumentInvalidException(nameof(dto), "Телеметрия уже была привязана к другой скважине."); if (dto.Id != 0 && (await GetCacheAsync(token)).Any(w => w.Id == dto.Id)) throw new ArgumentInvalidException(nameof(dto), $"Нельзя повторно добавить скважину с id: {dto.Id}"); var entity = Convert(dto); var result = await base.InsertAsync(dto, token); if (dto.Companies.Any()) { var newRelations = dto.Companies.Select(c => new RelationCompanyWell { IdWell = result, IdCompany = c.Id }); dbContext.RelationCompaniesWells.AddRange(newRelations); await dbContext.SaveChangesAsync(token); DropCacheRelationCompanyWell(); } return result; } public override Task InsertRangeAsync(IEnumerable dtos, CancellationToken token) { throw new NotImplementedException(); } public override async Task UpdateAsync(WellDto dto, CancellationToken token) { if (IsTelemetryAssignedToDifferentWell(dto)) throw new ArgumentInvalidException(nameof(dto), "Телеметрия уже была привязана к другой скважине."); var oldRelations = (await GetCacheRelationCompanyWellAsync(token)) .Where(r => r.IdWell == dto.Id).ToArray(); if (dto.Companies.Count() != oldRelations.Length || dto.Companies.Any(c => oldRelations.All(oldC => oldC.IdCompany != c.Id))) { dbContext.RelationCompaniesWells .RemoveRange(dbContext.RelationCompaniesWells .Where(r => r.IdWell == dto.Id)); DropCacheRelationCompanyWell(); var newRelations = dto.Companies .Select(c => new RelationCompanyWell { IdWell = dto.Id, IdCompany = c.Id }); dbContext.RelationCompaniesWells.AddRange(newRelations); } var result = await base.UpdateAsync(dto, token); return result; } public async Task IsCompanyInvolvedInWellAsync(int idCompany, int idWell, CancellationToken token) => (await GetCacheRelationCompanyWellAsync(token)) .Any(r => r.IdWell == idWell && r.IdCompany == idCompany); public async Task GetWellCaptionByIdAsync(int idWell, CancellationToken token) { var entity = await GetOrDefaultAsync(idWell, token).ConfigureAwait(false); return entity!.Caption; } public async Task> GetCompaniesAsync(int idWell, CancellationToken token) { var relations = (await GetCacheRelationCompanyWellAsync(token)) .Where(r => r.IdWell == idWell); var dtos = relations.Select(r => Convert(r.Company)); return dtos; } public string GetStateText(int state) { return state switch { 1 => "В работе", 2 => "Завершена", _ => "Неизвестно", }; } public async Task> GetClusterWellsIdsAsync(int idWell, CancellationToken token) { var well = await GetOrDefaultAsync(idWell, token); if (well is null) return Enumerable.Empty(); var cache = await GetCacheAsync(token); var clusterWellsIds = cache .Where((w) => w.IdCluster == well.IdCluster) .Select(w => w.Id); return clusterWellsIds; } protected override Well Convert(WellDto dto) { var entity = dto.Adapt(); entity.IdTelemetry = entity.IdTelemetry ?? dto.IdTelemetry ?? dto.Telemetry?.Id; if (dto.Timezone is null) entity.Timezone = GetTimezone(dto.Id) .Adapt(); return entity; } protected override WellDto Convert(Well entity) { var dto = base.Convert(entity); if (entity.Timezone is null) dto.Timezone = GetTimezone(entity.Id); dto.StartDate = wellOperationRepository .GetFirstAndLastFact(entity.Id)?.First?.DateStart; dto.WellType = entity.WellType.Caption; dto.Cluster = entity.Cluster.Caption; dto.Deposit = entity.Cluster.Deposit.Caption; if (entity.IdTelemetry is not null) dto.LastTelemetryDate = telemetryService.GetDatesRange(entity.IdTelemetry.Value).To.ToOffset(dto.Timezone.Offset); dto.Companies = entity.RelationCompaniesWells .Select(r => Convert(r.Company)) .ToList(); return dto; } private CompanyDto Convert(Company entity) { var dto = entity.Adapt(); dto.CompanyTypeCaption = entity.CompanyType?.Caption ?? companyTypesService.GetOrDefault(entity.IdCompanyType)?.Caption ?? string.Empty; return dto; } public SimpleTimezoneDto GetTimezone(int idWell) { var cache = GetCache(); var cacheItem = cache.FirstOrDefault(d => d.Id == idWell) ?? throw new ArgumentInvalidException(nameof(idWell), $"idWell: {idWell} does not exist."); return cacheItem.Timezone.Adapt(); } private bool IsTelemetryAssignedToDifferentWell(WellDto wellDto) { if (!wellDto.IdTelemetry.HasValue) return false; var existingWellWithAssignedTelemetry = GetCache() .FirstOrDefault(x => x.IdTelemetry == wellDto.IdTelemetry); if (existingWellWithAssignedTelemetry is null) return false; return existingWellWithAssignedTelemetry.Id != wellDto.Id; } private static AsbCloudDb.Model.IMapPoint GetCoordinates(Well well) { if (well is null) throw new ArgumentNullException(nameof(well)); if (well.Latitude is not null & well.Longitude is not null) return well; var cluster = well.Cluster; if (cluster.Latitude is not null & cluster.Longitude is not null) return cluster; if (cluster.Deposit is null) throw new Exception($"Can't find coordinates of well by cluster {cluster.Caption} id: {cluster.Id}"); var deposit = cluster.Deposit; if (deposit.Latitude is not null & deposit.Longitude is not null) return deposit; throw new Exception($"Can't find coordinates of well by deposit {deposit.Caption} id: {deposit.Id}"); } public DatesRangeDto GetDatesRange(int idWell) { var well = GetOrDefault(idWell); if (well is null) throw new Exception($"Well id: {idWell} does not exist."); if (well.IdTelemetry is null) throw new KeyNotFoundException($"Well id: {idWell} does not contain telemetry."); return telemetryService.GetDatesRange((int)well.IdTelemetry); } }