forked from ddrilling/AsbCloudServer
573 lines
25 KiB
C#
573 lines
25 KiB
C#
using AsbCloudApp.Data;
|
||
using AsbCloudApp.Data.User;
|
||
using AsbCloudApp.Exceptions;
|
||
using AsbCloudApp.Repositories;
|
||
using AsbCloudApp.Services;
|
||
using AsbCloudDb.Model;
|
||
using AsbCloudInfrastructure.Background;
|
||
using AsbCloudInfrastructure.Services.DrillingProgram.Convert;
|
||
using Mapster;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.Extensions.Configuration;
|
||
using Microsoft.Extensions.DependencyInjection;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using AsbCloudApp.Requests;
|
||
using AsbCloudApp.Services.Notifications;
|
||
using AsbCloudInfrastructure.Services.Email;
|
||
|
||
namespace AsbCloudInfrastructure.Services.DrillingProgram;
|
||
|
||
# nullable enable
|
||
public class DrillingProgramService : IDrillingProgramService
|
||
{
|
||
private static readonly Dictionary<string, DrillingProgramCreateError> drillingProgramCreateErrors = new Dictionary<string, DrillingProgramCreateError>();
|
||
|
||
private readonly IAsbCloudDbContext context;
|
||
private readonly FileService fileService;
|
||
private readonly IUserRepository userRepository;
|
||
private readonly IWellService wellService;
|
||
private readonly IConfiguration configuration;
|
||
private readonly BackgroundWorker backgroundWorker;
|
||
private readonly NotificationService notificationService;
|
||
|
||
private const int idTransportType = 1;
|
||
|
||
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;
|
||
private const int idStateError = 4;
|
||
|
||
private static readonly string[] validFileExtensions = ConvertToPdf.filesExtensions;
|
||
|
||
public DrillingProgramService(
|
||
IAsbCloudDbContext context,
|
||
FileService fileService,
|
||
IUserRepository userRepository,
|
||
IWellService wellService,
|
||
IConfiguration configuration,
|
||
BackgroundWorker backgroundWorker,
|
||
NotificationService notificationService)
|
||
{
|
||
this.context = context;
|
||
this.fileService = fileService;
|
||
this.userRepository = userRepository;
|
||
this.wellService = wellService;
|
||
this.configuration = configuration;
|
||
this.backgroundWorker = backgroundWorker;
|
||
this.notificationService = notificationService;
|
||
}
|
||
|
||
public async Task<IEnumerable<UserDto>> GetAvailableUsers(int idWell, CancellationToken token = default)
|
||
{
|
||
var users = await context.RelationCompaniesWells
|
||
.Include(r => r.Company)
|
||
.ThenInclude(c => c.Users)
|
||
.Where(r => r.IdWell == idWell)
|
||
.SelectMany(r => r.Company.Users)
|
||
.Where(u => u != null && !string.IsNullOrEmpty(u.Email))
|
||
.ToListAsync(token);
|
||
var usersDto = users.Adapt<IEnumerable<UserDto>>();
|
||
return usersDto;
|
||
}
|
||
|
||
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)
|
||
.ThenInclude(u => u.Company)
|
||
.Where(p => p.IdWell == idWell)
|
||
.ToListAsync(token);
|
||
|
||
var parts = new List<DrillingProgramPartDto>(partEntities.Count);
|
||
var timezoneOffset = wellService.GetTimezone(idWell)?.Hours ?? 5;
|
||
foreach (var partEntity in partEntities)
|
||
{
|
||
var part = ConvertPart(idUser, fileCategories, files, partEntity, timezoneOffset);
|
||
parts.Add(part);
|
||
}
|
||
|
||
var state = new DrillingProgramStateDto
|
||
{
|
||
Parts = parts,
|
||
Program = files.FirstOrDefault(f => f.IdCategory == idFileCategoryDrillingProgram)
|
||
?.Adapt<FileInfoDto>(),
|
||
PermissionToEdit = userRepository.HasPermission(idUser, "DrillingProgram.edit"),
|
||
};
|
||
|
||
if (parts.Any())
|
||
{
|
||
if (parts.All(p => p.IdState == idPartStateApproved))
|
||
{
|
||
if (state.Program is not null)
|
||
{
|
||
state.IdState = idStateReady;
|
||
}
|
||
else
|
||
{
|
||
var workId = MakeWorkId(idWell);
|
||
if (drillingProgramCreateErrors.ContainsKey(workId))
|
||
{
|
||
state.IdState = idStateError;
|
||
state.Error = drillingProgramCreateErrors[workId];
|
||
}
|
||
else
|
||
state.IdState = idStateCreating;
|
||
}
|
||
}
|
||
else
|
||
state.IdState = idStateApproving;
|
||
}
|
||
else
|
||
state.IdState = idStateNotInitialized;
|
||
|
||
await EnqueueMakeProgramWorkAsync(idWell, state, token);
|
||
return state;
|
||
}
|
||
|
||
private static bool IsFileExtensionValid(string file)
|
||
{
|
||
var fileExt = Path.GetExtension(file).ToLower();
|
||
return validFileExtensions.Contains(fileExt);
|
||
}
|
||
|
||
public async Task<int> AddFile(int idWell, int idFileCategory, int idUser, string fileFullName, Stream fileStream, CancellationToken token = default)
|
||
{
|
||
if (!IsFileExtensionValid(fileFullName))
|
||
throw new FileFormatException($"Файл {fileFullName} - не поддерживаемого формата. Файл не может быть загружен.");
|
||
|
||
var part = await context.DrillingProgramParts
|
||
.Include(p => p.RelatedUsers)
|
||
.ThenInclude(r => r.User)
|
||
.FirstOrDefaultAsync(p => p.IdWell == idWell && p.IdFileCategory == idFileCategory, token)
|
||
?? throw new ArgumentInvalidException(nameof(idFileCategory), $"DrillingProgramPart id == {idFileCategory} does not exist");
|
||
|
||
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);
|
||
|
||
await NotifyApproversAsync(part, result.Id, fileFullName, token);
|
||
|
||
return result.Id;
|
||
}
|
||
|
||
public async Task<int> AddPartsAsync(int idWell, IEnumerable<int> idFileCategories, CancellationToken token = default)
|
||
{
|
||
if (!idFileCategories.Any())
|
||
return 0;
|
||
|
||
var existingCategories = await context.DrillingProgramParts
|
||
.Where(p => p.IdWell == idWell)
|
||
.Select(p => p.IdFileCategory)
|
||
.ToListAsync(token);
|
||
|
||
var newParts = idFileCategories
|
||
.Where(c => !existingCategories.Any(e => e == c))
|
||
.Select(c => new DrillingProgramPart
|
||
{
|
||
IdWell = idWell,
|
||
IdFileCategory = c,
|
||
});
|
||
|
||
context.DrillingProgramParts.AddRange(newParts);
|
||
var affected = await context.SaveChangesAsync(token);
|
||
|
||
await RemoveDrillingProgramAsync(idWell, token);
|
||
return affected;
|
||
}
|
||
|
||
public async Task<int> RemovePartsAsync(int idWell, IEnumerable<int> idFileCategories, CancellationToken token = default)
|
||
{
|
||
var whereQuery = context.DrillingProgramParts
|
||
.Where(p => p.IdWell == idWell && idFileCategories.Contains(p.IdFileCategory));
|
||
|
||
context.DrillingProgramParts.RemoveRange(whereQuery);
|
||
|
||
await RemoveDrillingProgramAsync(idWell, token);
|
||
return await context.SaveChangesAsync(token);
|
||
}
|
||
|
||
public async Task<int> AddUserAsync(int idWell, int idFileCategory, int idUser, int idUserRole, CancellationToken token = default)
|
||
{
|
||
var user = await userRepository.GetOrDefaultAsync(idUser, token)
|
||
?? throw new ArgumentInvalidException(nameof(idUser), $"User id: {idUser} does not exist");
|
||
|
||
var part = await context.DrillingProgramParts
|
||
.Include(p => p.FileCategory)
|
||
.FirstOrDefaultAsync(p => p.IdWell == idWell && p.IdFileCategory == idFileCategory, token)
|
||
?? throw new ArgumentInvalidException(nameof(idFileCategory), $"DrillingProgramPart idFileCategory: {idFileCategory} does not exist");
|
||
|
||
if (idUserRole != idUserRoleApprover && idUserRole != idUserRolePublisher)
|
||
throw new ArgumentInvalidException(nameof(idUserRole), $"idUserRole ({idUserRole}), should be approver ({idUserRoleApprover}) or publisher ({idUserRolePublisher})");
|
||
|
||
var oldRelation = await context.RelationDrillingProgramPartUsers
|
||
.FirstOrDefaultAsync(r => r.IdUser == idUser && r.IdDrillingProgramPart == part.Id, token);
|
||
|
||
if (oldRelation is not null)
|
||
context.RelationDrillingProgramPartUsers.Remove(oldRelation);
|
||
|
||
var newRelation = new RelationUserDrillingProgramPart
|
||
{
|
||
IdUser = idUser,
|
||
IdDrillingProgramPart = part.Id,
|
||
IdUserRole = idUserRole,
|
||
};
|
||
context.RelationDrillingProgramPartUsers.Add(newRelation);
|
||
if (idUserRole == idUserRoleApprover)
|
||
await RemoveDrillingProgramAsync(part.IdWell, token);
|
||
|
||
if (idUserRole == idUserRolePublisher)
|
||
await NotifyNewPublisherAsync(idWell, user, part.FileCategory.Name, token);
|
||
|
||
return await context.SaveChangesAsync(token);
|
||
}
|
||
|
||
public async Task<int> RemoveUserAsync(int idWell, int idFileCategory, int idUser, int idUserRole, CancellationToken token = default)
|
||
{
|
||
var whereQuery = context.RelationDrillingProgramPartUsers
|
||
.Include(r => r.DrillingProgramPart)
|
||
.Where(r => r.IdUser == idUser &&
|
||
r.IdUserRole == idUserRole &&
|
||
r.DrillingProgramPart.IdWell == idWell &&
|
||
r.DrillingProgramPart.IdFileCategory == idFileCategory);
|
||
|
||
context.RelationDrillingProgramPartUsers.RemoveRange(whereQuery);
|
||
|
||
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.GetOrDefaultAsync(fileMarkDto.IdFile, token)
|
||
?? throw new ArgumentInvalidException(nameof(fileMarkDto), $"Файла для такой отметки не существует.");
|
||
|
||
if (fileInfo.IdCategory < idFileCategoryDrillingProgramPartsStart ||
|
||
fileInfo.IdCategory > idFileCategoryDrillingProgramPartsEnd)
|
||
throw new ArgumentInvalidException(nameof(fileMarkDto), $"Этот метод допустим только для файлов-частей программы бурения.");
|
||
|
||
var part = await context.DrillingProgramParts
|
||
.Include(p => p.RelatedUsers)
|
||
.ThenInclude(r => r.User)
|
||
.AsNoTracking()
|
||
.FirstOrDefaultAsync(p => p.IdWell == fileInfo.IdWell && p.IdFileCategory == fileInfo.IdCategory, token);
|
||
|
||
var user = part?.RelatedUsers.FirstOrDefault(r => r.IdUser == idUser && r.IdUserRole == idUserRoleApprover)?.User
|
||
?? throw new ForbidException($"User {idUser} is not in the approvers list.");
|
||
|
||
fileMarkDto.User = user.Adapt<UserDto>();
|
||
|
||
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 == idMarkTypeReject)
|
||
{
|
||
await RemoveDrillingProgramAsync(fileInfo.IdWell, token);
|
||
await NotifyPublisherOnRejectAsync(fileMarkDto, token);
|
||
}
|
||
else
|
||
{
|
||
// если все согласованты согласовали - оповещаем публикатора
|
||
var approvers = part!.RelatedUsers
|
||
.Where(u => u.IdUserRole == idUserRoleApprover);
|
||
if (approvers
|
||
.All(user => fileInfo.FileMarks
|
||
?.Any(mark => (mark.IdMarkType == idMarkTypeApprove && mark.User?.Id == user.IdUser && !mark.IsDeleted)) == true ||
|
||
(fileMarkDto.IdMarkType == idMarkTypeApprove && user.IdUser == idUser)))
|
||
{
|
||
await NotifyPublisherOnFullAccepAsync(fileMarkDto, 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 async Task NotifyPublisherOnFullAccepAsync(FileMarkDto fileMark, CancellationToken token)
|
||
{
|
||
var file = await fileService.GetOrDefaultAsync(fileMark.IdFile, token);
|
||
var well = await wellService.GetOrDefaultAsync(file!.IdWell, token)
|
||
?? throw new ArgumentInvalidException(nameof(file.IdWell), "idWell doesn`t exist");
|
||
|
||
var user = file.Author!;
|
||
var factory = new DrillingMailBodyFactory(configuration);
|
||
var subject = factory.MakeSubject(well, "Загруженный вами документ полностью согласован");
|
||
var body = factory.MakeMailBodyForPublisherOnFullAccept(well, user.Name ?? string.Empty, file.Id, file.Name);
|
||
|
||
await notificationService.NotifyAsync(new NotifyRequest
|
||
{
|
||
IdUser = user.Id,
|
||
IdNotificationCategory = NotificationCategory.IdSystemNotificationCategory,
|
||
Title = subject,
|
||
Message = body,
|
||
IdTransportType = idTransportType
|
||
}, token);
|
||
}
|
||
|
||
private async Task NotifyPublisherOnRejectAsync(FileMarkDto fileMark, CancellationToken token)
|
||
{
|
||
var file = await fileService.GetOrDefaultAsync(fileMark.IdFile, token);
|
||
var well = await wellService.GetOrDefaultAsync(file!.IdWell, token)
|
||
?? throw new ArgumentInvalidException(nameof(file.IdWell), "idWell doesn`t exist");
|
||
|
||
var user = file.Author!;
|
||
var factory = new DrillingMailBodyFactory(configuration);
|
||
var subject = factory.MakeSubject(well, "Загруженный вами документ отклонен");
|
||
var body = factory.MakeMailBodyForPublisherOnReject(well, user.Name ?? string.Empty, file.Id, file.Name, fileMark);
|
||
|
||
await notificationService.NotifyAsync(new NotifyRequest
|
||
{
|
||
IdUser = user.Id,
|
||
IdNotificationCategory = NotificationCategory.IdSystemNotificationCategory,
|
||
Title = subject,
|
||
Message = body,
|
||
IdTransportType = idTransportType
|
||
}, token);
|
||
}
|
||
|
||
private async Task NotifyApproversAsync(DrillingProgramPart part, int idFile, string fileName, CancellationToken token)
|
||
{
|
||
var well = await wellService.GetOrDefaultAsync(part.IdWell, token)
|
||
?? throw new ArgumentInvalidException(nameof(part.IdWell), "idWell doesn`t exist");
|
||
|
||
var factory = new DrillingMailBodyFactory(configuration);
|
||
var subject = factory.MakeSubject(well, "Загружен новый документ для согласования.");
|
||
var users = part.RelatedUsers
|
||
.Where(r => r.IdUserRole == idUserRoleApprover)
|
||
.Select(r => r.User);
|
||
|
||
foreach (var user in users)
|
||
{
|
||
var body = factory.MakeMailBodyForApproverNewFile(well, user.Name ?? string.Empty, idFile, fileName);
|
||
await notificationService.NotifyAsync(new NotifyRequest
|
||
{
|
||
IdUser = user.Id,
|
||
IdNotificationCategory = NotificationCategory.IdSystemNotificationCategory,
|
||
Title = subject,
|
||
Message = body,
|
||
IdTransportType = idTransportType
|
||
}, token);
|
||
}
|
||
}
|
||
|
||
private async Task NotifyNewPublisherAsync(int idWell, UserDto user, string documentCategory, CancellationToken token)
|
||
{
|
||
var well = await wellService.GetOrDefaultAsync(idWell, token)
|
||
?? throw new ArgumentInvalidException(nameof(idWell), "idWell doesn`t exist");
|
||
|
||
var factory = new DrillingMailBodyFactory(configuration);
|
||
var subject = factory.MakeSubject(well, $"От вас ожидается загрузка на портал документа «{documentCategory}»");
|
||
var body = factory.MakeMailBodyForNewPublisher(well, user.Name ?? string.Empty, documentCategory);
|
||
|
||
await notificationService.NotifyAsync(new NotifyRequest
|
||
{
|
||
IdUser = user.Id,
|
||
IdNotificationCategory = NotificationCategory.IdSystemNotificationCategory,
|
||
Title = subject,
|
||
Message = body,
|
||
IdTransportType = idTransportType
|
||
}, token);
|
||
}
|
||
|
||
private static DrillingProgramPartDto ConvertPart(int idUser, List<FileCategory> fileCategories, List<AsbCloudDb.Model.FileInfo> files, DrillingProgramPart partEntity, double timezoneOffset)
|
||
{
|
||
var part = new DrillingProgramPartDto
|
||
{
|
||
IdFileCategory = partEntity.IdFileCategory,
|
||
Name = fileCategories.FirstOrDefault(c => c.Id == partEntity.IdFileCategory)!.Name,
|
||
Approvers = partEntity.RelatedUsers
|
||
.Where(r => r.IdUserRole == idUserRoleApprover)
|
||
.Select(r => r.User.Adapt<UserDto>()),
|
||
Publishers = partEntity.RelatedUsers
|
||
.Where(r => r.IdUserRole == idUserRolePublisher)
|
||
.Select(r => r.User.Adapt<UserDto>()),
|
||
PermissionToApprove = partEntity.RelatedUsers
|
||
.Any(r => r.IdUserRole == idUserRoleApprover && r.IdUser == idUser),
|
||
PermissionToUpload = partEntity.RelatedUsers
|
||
.Any(r => r.IdUserRole == idUserRolePublisher && r.IdUser == idUser),
|
||
};
|
||
|
||
var fileEntity = files.LastOrDefault(f => f.IdCategory == partEntity.IdFileCategory);
|
||
|
||
if (fileEntity is not null)
|
||
{
|
||
part.IdState = idPartStateApproving;
|
||
part.File = new FileInfoDto
|
||
{
|
||
Id = fileEntity.Id,
|
||
Author = fileEntity.Author?.Adapt<UserDto>(),
|
||
IdAuthor = fileEntity.Author?.Id,
|
||
IdCategory = fileEntity.IdCategory,
|
||
IdWell = fileEntity.IdWell,
|
||
Name = fileEntity.Name,
|
||
Size = fileEntity.Size,
|
||
UploadDate = fileEntity.UploadDate.ToOffset(TimeSpan.FromHours(timezoneOffset)),
|
||
};
|
||
|
||
var marks = fileEntity.FileMarks?.Where(m => !m.IsDeleted);
|
||
if (marks?.Any() == true)
|
||
{
|
||
part.File.FileMarks = marks.Select(m =>
|
||
{
|
||
var mark = m.Adapt<FileMarkDto>();
|
||
mark.DateCreated = m.DateCreated.ToOffset(TimeSpan.FromHours(timezoneOffset));
|
||
return mark;
|
||
});
|
||
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 = idPartStateNoFile;
|
||
|
||
return part;
|
||
}
|
||
|
||
private async Task EnqueueMakeProgramWorkAsync(int idWell, DrillingProgramStateDto state, CancellationToken token)
|
||
{
|
||
if (state.IdState == idStateCreating)
|
||
{
|
||
var workId = MakeWorkId(idWell);
|
||
if (!backgroundWorker.Works.Any(w => w.Id == workId))
|
||
{
|
||
var well = (await wellService.GetOrDefaultAsync(idWell, token))!;
|
||
var resultFileName = $"Программа бурения {well.Cluster} {well.Caption}.pdf";
|
||
var convertedFilesDir = Path.Combine(Path.GetTempPath(), "drillingProgram", $"{well.Cluster}_{well.Caption}");
|
||
var tempResultFilePath = Path.Combine(convertedFilesDir, resultFileName);
|
||
var workAction = async (string workId, IServiceProvider serviceProvider, Action<string, double?> onProgress, CancellationToken token) =>
|
||
{
|
||
var context = serviceProvider.GetRequiredService<IAsbCloudDbContext>();
|
||
var fileService = serviceProvider.GetRequiredService<FileService>();
|
||
var files = state.Parts.Select(p => fileService.GetUrl(p.File!));
|
||
onProgress($"Start converting {files.Count()} files to PDF.", null);
|
||
await ConvertToPdf.GetConverteAndMergedFileAsync(files, tempResultFilePath, convertedFilesDir, token);
|
||
await fileService.MoveAsync(idWell, null, idFileCategoryDrillingProgram, resultFileName, tempResultFilePath, token);
|
||
};
|
||
|
||
var onErrorAction = (string workId, Exception exception, CancellationToken token) =>
|
||
{
|
||
var message = $"Не удалось сформировать программу бурения по скважине {well?.Caption}";
|
||
drillingProgramCreateErrors[workId] = new()
|
||
{
|
||
Message = message,
|
||
Exception = exception.Message,
|
||
};
|
||
return Task.CompletedTask;
|
||
};
|
||
|
||
var work = Work.CreateByDelegate(workId, workAction);
|
||
work.OnErrorAsync = onErrorAction;
|
||
backgroundWorker.Enqueue(work);
|
||
}
|
||
}
|
||
}
|
||
|
||
public void ClearError(int idWell)
|
||
{
|
||
var workId = MakeWorkId(idWell);
|
||
drillingProgramCreateErrors.Remove(workId);
|
||
}
|
||
|
||
private async Task<int> RemoveDrillingProgramAsync(int idWell, CancellationToken token)
|
||
{
|
||
var workId = MakeWorkId(idWell);
|
||
backgroundWorker.TryRemoveFromQueue(workId);
|
||
|
||
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;
|
||
}
|
||
|
||
private static string MakeWorkId(int idWell)
|
||
=> $"Make drilling program for wellId {idWell}";
|
||
} |