DD.WellWorkover.Cloud/AsbCloudInfrastructure/Services/WellService.cs
Степанов Дмитрий 28788bde1f Фиксы
2024-09-02 10:04:36 +05:00

355 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<WellDto, Well>, IWellService
{
private readonly ITelemetryService telemetryService;
private readonly ICrudRepository<CompanyTypeDto> companyTypesService;
private readonly ITimezoneService timezoneService;
private readonly WellInfoService wellInfoService;
private readonly IWellOperationRepository wellOperationRepository;
public ITelemetryService TelemetryService => telemetryService;
private static IQueryable<Well> MakeQueryWell(DbSet<Well> 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<CompanyTypeDto, CompanyType>(dbContext, memoryCache);
}
private Task<IEnumerable<RelationCompanyWell>> GetCacheRelationCompanyWellAsync(CancellationToken token)
{
return memoryCache.GetOrCreateBasicAsync(
dbContext.Set<RelationCompanyWell>()
.Include(r => r.Company)
.Include(r => r.Well)
, token);
}
private void DropCacheRelationCompanyWell()
=> memoryCache.DropBasic<RelationCompanyWell>();
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;
}
/// <inheritdoc/>
public async Task<IEnumerable<DepositBranchDto>> 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<WellMapInfoWithTelemetryStat>();
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<WellMapInfoWithTelemetryStat?> 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<WellMapInfoWithTelemetryStat>();
wellInfo.IdState = well.IdState;
return wellInfo;
}
public async Task<IEnumerable<WellDto>> GetAsync(WellRequest request, CancellationToken token)
{
var wells = await GetEntitiesAsync(request, token);
var wellsDtos = wells.Select(Convert);
return wellsDtos;
}
private async Task<IEnumerable<Well>> 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<int> 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<int> InsertRangeAsync(IEnumerable<WellDto> dtos, CancellationToken token)
{
throw new NotImplementedException();
}
public override async Task<int> 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<bool> IsCompanyInvolvedInWellAsync(int idCompany, int idWell, CancellationToken token)
=> (await GetCacheRelationCompanyWellAsync(token))
.Any(r => r.IdWell == idWell && r.IdCompany == idCompany);
public async Task<string> GetWellCaptionByIdAsync(int idWell, CancellationToken token)
{
var entity = await GetOrDefaultAsync(idWell, token).ConfigureAwait(false);
return entity!.Caption;
}
public async Task<IEnumerable<CompanyDto>> 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<IEnumerable<int>> GetClusterWellsIdsAsync(int idWell, CancellationToken token)
{
var well = await GetOrDefaultAsync(idWell, token);
if (well is null)
return Enumerable.Empty<int>();
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<Well>();
entity.IdTelemetry = entity.IdTelemetry ?? dto.IdTelemetry ?? dto.Telemetry?.Id;
if (dto.Timezone is null)
entity.Timezone = GetTimezone(dto.Id)
.Adapt<SimpleTimezone>();
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<CompanyDto>();
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<SimpleTimezoneDto>();
}
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);
}
}