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); public WellService(IAsbCloudDbContext db, IMemoryCache memoryCache, ITelemetryService telemetryService, ITimezoneService timezoneService, WellInfoService wellInfoService) : base(db, memoryCache, MakeQueryWell) { this.telemetryService = telemetryService; this.timezoneService = timezoneService; this.wellInfoService = wellInfoService; this.wellOperationRepository = new WellOperationRepository(db, memoryCache, 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 DateTime GetLastTelemetryDate(int idWell) { var well = GetOrDefault(idWell); if (well?.IdTelemetry is null) return DateTime.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; return dto; }), }), }); return depositTree; } public async Task GetOrDefaultStatAsync(int idWell, CancellationToken token) { var dto = wellInfoService.FirstOrDefault(well => well.Id == idWell); if (dto is not null) return dto; var request = new WellRequest{Ids = new[] { idWell }}; var entities = await GetEntitiesAsync(request, token); var entity = entities.FirstOrDefault(); if (entity is null) return null; dto = entity.Adapt(); return dto; } 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.IdWellType is < 1 or > 2) throw new ArgumentInvalidException("Тип скважины указан неправильно.", nameof(dto)); if (dto.IdState is < 0 or > 2) throw new ArgumentInvalidException("Текущее состояние работы скважины указано неправильно.", nameof(dto)); if (dto.Id != 0 && (await GetCacheAsync(token)).Any(w => w.Id == dto.Id)) throw new ArgumentInvalidException($"Нельзя повторно добавить скважину с id: {dto.Id}", nameof(dto)); 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)); if (dto.IdWellType is < 1 or > 2) throw new ArgumentInvalidException("Тип скважины указан неправильно.", nameof(dto)); if (dto.IdState is < 0 or > 2) 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.FirstOperationDate(entity.Id)?.ToRemoteDateTime(dto.Timezone.Hours); 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; 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 well = GetOrDefault(idWell); if (well == null) throw new ArgumentInvalidException($"idWell: {idWell} does not exist.", nameof(idWell)); return GetTimezone(well); } private SimpleTimezoneDto GetTimezone(WellDto wellDto) { if (wellDto.Timezone is not null) return wellDto.Timezone; if (wellDto.Telemetry is not null) { var timezone = telemetryService.GetTimezone(wellDto.Telemetry.Id); if (timezone is not null) return timezone; } var well = GetQuery().FirstOrDefault(w => w.Id == wellDto.Id); if (well is not null) { var point = GetCoordinates(well); if (point is not null) { if (point.Timezone is not null) { return point.Timezone.Adapt(); } if (point.Latitude is not null & point.Longitude is not null) { var timezone = timezoneService.GetOrDefaultByCoordinates(point.Latitude!.Value, point.Longitude!.Value); if (timezone is not null) { return timezone; } } } } throw new Exception($"Can't find timezone for well {wellDto.Caption} id: {wellDto.Id}"); } 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); } } }