DD.WellWorkover.Cloud/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs

339 lines
15 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.Services;
using AsbCloudDb.Model;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services.DrillingProgram
{
public class DrillingProgramService : IDrillingProgramService
{
private readonly IAsbCloudDbContext context;
private readonly IFileService fileService;
private readonly IUserService userService;
private const int idFileCategoryDrillingProgram = 1000;
private const int idFileCategoryDrillingProgramPartsStart = 1001;
private const int idFileCategoryDrillingProgramPartsEnd = 1100;
private const int idPartStateNoFile = 0;
private const int idPartStateApproving = 1;
private const int idPartStateApproved = 2;
private const int idMarkTypeReject = 0;
private const int idMarkTypeApprove = 1;
private const int idUserRolePublisher = 1;
private const int idUserRoleApprover = 2;
private const int idStateNotInitialized = 0;
private const int idStateApproving = 1;
private const int idStateCreating = 2;
private const int idStateReady = 3;
public DrillingProgramService(IAsbCloudDbContext context, IFileService fileService, IUserService userService)
{
this.context = context;
this.fileService = fileService;
this.userService = userService;
}
public async Task<IEnumerable<FileCategoryDto>> GetCategoriesAsync(CancellationToken token = default)
{
var result = await context.FileCategories
.Where(c=>c.Id > idFileCategoryDrillingProgramPartsStart && c.Id < idFileCategoryDrillingProgramPartsEnd)
.ToListAsync(token);
return result.Select(c => c.Adapt<FileCategoryDto>());
}
public async Task<DrillingProgramStateDto> GetStateAsync(int idWell, int idUser, CancellationToken token = default)
{
// Задание от геологов
// Профиль ствола скважины(ННБ)
// Технологические расчеты(ННБ)
// Долотная программа
// Программа по растворам
// Программа геофизических исследований
// Планы спусков обсадных колонн
// Программы цементирования обсадных колонн
var fileCategories = await context.FileCategories
.Where(c => c.Id >= idFileCategoryDrillingProgramPartsStart &&
c.Id < idFileCategoryDrillingProgramPartsEnd)
.ToListAsync(token);
var files = await context.Files
.Include(f => f.FileMarks)
.ThenInclude(m => m.User)
.Include(f => f.Author)
.Include(f => f.FileCategory)
.Where(f => f.IdWell == idWell &&
f.IdCategory >= idFileCategoryDrillingProgram &&
f.IdCategory < idFileCategoryDrillingProgramPartsEnd &&
f.IsDeleted == false)
.OrderBy(f => f.UploadDate)
.ToListAsync(token);
var partEntities = await context.DrillingProgramParts
.Include(p => p.RelatedUsers)
.ThenInclude(r => r.User)
.Where(p => p.IdWell == idWell)
.ToListAsync(token);
var parts = new List<DrillingProgramPartDto>(partEntities.Count);
foreach (var partEntity in partEntities)
{
var part = ConvertPart(idUser, fileCategories, files, partEntity);
parts.Add(part);
}
var state = new DrillingProgramStateDto();
state.Parts = parts;
state.Program = files.FirstOrDefault(f => f.IdCategory == idFileCategoryDrillingProgram)
.Adapt<FileInfoDto>();
if (parts.Any())
{
if(parts.All(p=>p.IdState == idPartStateApproved))
{
if (state.Program is not null)
state.IdState = idStateReady;
else
state.IdState = idStateCreating;
}
else
state.IdState = idStateApproving;
}
else
state.IdState = idStateNotInitialized;
return state;
}
public async Task<int> AddFile(int idPart, int idUser, string fileFullName, System.IO.Stream fileStream, CancellationToken token = default)
{
var part = await context.DrillingProgramParts
.Include(p => p.RelatedUsers)
.FirstOrDefaultAsync(p => p.Id == idPart);
if (part == null)
throw new ArgumentInvalidException($"DrillingProgramPart id == {idPart} does not exist", nameof(idPart));
if (! part.RelatedUsers.Any(r => r.IdUser == idUser && r.IdUserRole == idUserRolePublisher))
throw new ForbidException($"User {idUser} is not in the publisher list.");
var result = await fileService.SaveAsync(
part.IdWell,
idUser,
part.IdFileCategory,
fileFullName,
fileStream,
token);
await RemoveDrillingProgramAsync(part.IdWell, token);
return result.Id;
}
public async Task<int> AddPartAsync(int idWell, int idFileCategory, CancellationToken token = default)
{
var part = new DrillingProgramPart
{
IdWell = idWell,
IdFileCategory = idFileCategory,
};
var entry = context.DrillingProgramParts.Add(part);
await context.SaveChangesAsync(token);
await RemoveDrillingProgramAsync(part.IdWell, token);
return entry.Entity.Id;
}
public async Task<int> RemovePartAsync(int idWell, int idFileCategory, CancellationToken token = default)
{
var whereQuery = context.DrillingProgramParts
.Where(r => r.IdWell == idWell &&
r.IdFileCategory == idFileCategory);
context.DrillingProgramParts.RemoveRange(whereQuery);
await RemoveDrillingProgramAsync(idWell, token);
return await context.SaveChangesAsync(token);
}
public async Task<int> AddUserAsync(int idUser, int idPart, int idUserRole, CancellationToken token = default)
{
var user = await userService.GetAsync(idUser, token);
if (user is null)
throw new ArgumentInvalidException($"User id == {idUser} does not exist", nameof(idUser));
var drillingProgramPart = await context.DrillingProgramParts.FirstOrDefaultAsync(p => p.Id == idPart, token);
if (drillingProgramPart is null)
throw new ArgumentInvalidException($"DrillingProgramPart id == {idPart} does not exist", nameof(idPart));
if (idUserRole != idUserRoleApprover && idUserRole != idUserRolePublisher)
throw new ArgumentInvalidException($"idUserRole ({idPart}), should be approver ({idUserRoleApprover}) or publisher({idUserRolePublisher})", nameof(idPart));
var newRelation = new RelationUserDrillingProgramPart
{
IdUser = idUser,
IdDrillingProgramPart = idPart,
IdUserRole = idUserRole,
};
context.RelationDrillingProgramPartUsers.Add(newRelation);
if(idUserRole == idUserRoleApprover)
await RemoveDrillingProgramAsync(drillingProgramPart.IdWell, token);
return await context.SaveChangesAsync(token);
}
public async Task<int> RemoveUserAsync(int idUser, int idPart, int idUserRole, CancellationToken token = default)
{
var whereQuery = context.RelationDrillingProgramPartUsers
.Where(r => r.IdUser == idUser &&
r.IdDrillingProgramPart == idPart &&
r.IdUserRole == idUserRole);
context.RelationDrillingProgramPartUsers.RemoveRange(whereQuery);
if (idUserRole == idUserRoleApprover)
{
var part = await context.DrillingProgramParts.FirstOrDefaultAsync(p => p.Id == idPart, token);
await CheckAndEnqueueMakeProgramAsync(part.IdWell, token);
}
return await context.SaveChangesAsync(token);
}
public async Task<int> AddOrReplaceFileMarkAsync(FileMarkDto fileMarkDto, int idUser, CancellationToken token)
{
if(fileMarkDto.IdMarkType != idMarkTypeApprove &&
fileMarkDto.IdMarkType != idMarkTypeReject)
throw new ArgumentInvalidException($"В этом методе допустимы только отметки о принятии или отклонении.", nameof(fileMarkDto));
var fileInfo = await fileService.GetInfoAsync(fileMarkDto.IdFile, token)
.ConfigureAwait(false);
if (fileInfo is null)
throw new ArgumentInvalidException($"Файла для такой отметки не существует.", nameof(fileMarkDto));
if (fileInfo.IdCategory < idFileCategoryDrillingProgramPartsStart ||
fileInfo.IdCategory > idFileCategoryDrillingProgramPartsEnd)
throw new ArgumentInvalidException($"Этот метод допустим только для файлов-частей программы бурения.", nameof(fileMarkDto));
var part = await context.DrillingProgramParts
.FirstOrDefaultAsync(p => p.IdWell == fileInfo.IdWell && p.IdFileCategory == fileInfo.IdCategory, token);
if (!part.RelatedUsers.Any(r => r.IdUser == idUser && r.IdUserRole == idUserRoleApprover))
throw new ForbidException($"User {idUser} is not in the approvers list.");
var oldMarksIds = fileInfo.FileMarks
?.Where(m => m.User.Id == idUser)
.Select(m => m.Id);
if(oldMarksIds?.Any() == true)
await fileService.MarkFileMarkAsDeletedAsync(oldMarksIds, token);
var result = await fileService.CreateFileMarkAsync(fileMarkDto, idUser, token)
.ConfigureAwait(false);
if(fileMarkDto.IdMarkType == idMarkTypeApprove)
await CheckAndEnqueueMakeProgramAsync(fileInfo.IdWell, token);
else
await RemoveDrillingProgramAsync(fileInfo.IdWell, token);
return result;
}
public async Task<int> MarkAsDeletedFileMarkAsync(int idMark,
CancellationToken token)
{
var fileInfo = await fileService.GetByMarkId(idMark, token)
.ConfigureAwait(false);
if (fileInfo.IdCategory < idFileCategoryDrillingProgramPartsStart ||
fileInfo.IdCategory > idFileCategoryDrillingProgramPartsEnd)
throw new ArgumentInvalidException($"Этот метод допустим только для файлов-частей программы бурения.", nameof(idMark));
var result = await fileService.MarkFileMarkAsDeletedAsync(idMark, token)
.ConfigureAwait(false);
await RemoveDrillingProgramAsync(fileInfo.IdWell, token);
return result;
}
private static DrillingProgramPartDto ConvertPart(int idUser, List<FileCategory> fileCategories, List<FileInfo> files, DrillingProgramPart partEntity)
{
var part = new DrillingProgramPartDto
{
IdFileCategory = partEntity.IdFileCategory,
Name = fileCategories.FirstOrDefault(c => c.Id == partEntity.IdFileCategory).Name,
Approvers = partEntity.RelatedUsers
.Where(u => u.IdUserRole == idUserRoleApprover)
.Select(u => u.Adapt<UserDto>()),
Publishers = partEntity.RelatedUsers
.Where(u => u.IdUserRole == idUserRolePublisher)
.Select(u => u.Adapt<UserDto>()),
PermissionToApprove = partEntity.RelatedUsers
.Any(u => u.IdUserRole == idUserRoleApprover && u.IdUser == idUser),
PermissionToUpload = partEntity.RelatedUsers
.Any(u => u.IdUserRole == idUserRolePublisher && u.IdUser == idUser),
};
var fileEntity = files.LastOrDefault(f => f.IdCategory == partEntity.IdFileCategory);
if (fileEntity is not null)
{
part.File = fileEntity.Adapt<FileInfoDto>();
var marks = fileEntity.FileMarks.Where(m => !m.IsDeleted);
if (marks.Any())
{
var hasReject = marks.Any(m => m.IdMarkType == idMarkTypeReject);
if (!hasReject)
{
var allAproved = part.Approvers.All(a => marks.Any(m => m.IdUser == a.Id && m.IdMarkType == idMarkTypeApprove));
if (allAproved)
part.IdState = idPartStateApproved;
}
}
else
part.IdState = idPartStateApproving;
}
else
part.IdState = idPartStateNoFile;
return part;
}
private async Task CheckAndEnqueueMakeProgramAsync(int idWell, CancellationToken token)
{
var state = await GetStateAsync(idWell, 0, token);
if(state.IdState == idStateCreating)
{
// TODO: check if task is running else enqueue task
throw new NotImplementedException();
}
}
private async Task<int> RemoveDrillingProgramAsync(int idWell, CancellationToken token)
{
// TODO: dequeue task if it exist
var filesIds = await context.Files
.Where(f => f.IdWell == idWell &&
f.IdCategory == idFileCategoryDrillingProgram)
.Select(f => f.Id)
.ToListAsync(token);
if (filesIds.Any())
return await fileService.DeleteAsync(filesIds, token);
else
return 0;
}
}
}