forked from ddrilling/AsbCloudServer
339 lines
15 KiB
C#
339 lines
15 KiB
C#
|
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;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|