DrillingProgramService существенно переработан.

This commit is contained in:
Фролов 2022-02-12 11:28:16 +05:00
parent 4c68045398
commit e4e906c8d7
30 changed files with 11006 additions and 344 deletions

View File

@ -5,8 +5,14 @@ namespace AsbCloudApp.Data
public class DrillingProgramPartDto
{
public string Name { get; set; }
public string Description { get; set; }
public int IdFileCategory { get; set; }
/// <summary>
/// 0 - NoFile
/// 1 - approving
/// 2 - completely approved
/// </summary>
public int IdState { get; set; }
public IEnumerable<UserDto> Publishers { get; set; }
public IEnumerable<UserDto> Approvers { get; set; }
public bool PermissionToApprove { get; set; }

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AsbCloudApp.Data
{
public class FileCategoryDto : IId
{
public int Id { get; set; }
public string Name { get; set; }
public string ShortName { get; set; }
}
}

View File

@ -6,6 +6,10 @@ namespace AsbCloudApp.Data
{
public int Id { get; set; }
public int IdFile { get; set; }
/// <summary>
/// 0 - отклонен
/// 1 - согласован
/// </summary>
public int IdMarkType { get; set; }
/// <summary>
/// äàòà/âðåìÿ äîáàâëåíèÿ.

View File

@ -2,6 +2,9 @@
namespace AsbCloudApp.Exceptions
{
/// <summary>
/// Argument validation fail Exception
/// </summary>
public class ArgumentInvalidException: Exception
{
public string ParamName { get; }
@ -11,5 +14,15 @@ namespace AsbCloudApp.Exceptions
{
ParamName = paramName;
}
public object ToValaidationErrorObject()
=> MakeValidationError(ParamName, Message);
public static object MakeValidationError(string paramName, params string[] errors)
=> new
{
name = paramName,
errors = errors,
};
}
}

View File

@ -0,0 +1,17 @@
using System;
namespace AsbCloudApp.Exceptions
{
/// <summary>
/// Access denied exception
/// </summary>
public class ForbidException: Exception
{
public ForbidException()
:base(){}
public ForbidException(string message)
:base(message){}
}
}

View File

@ -1,4 +1,6 @@
using AsbCloudApp.Data;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@ -6,9 +8,15 @@ namespace AsbCloudApp.Services
{
public interface IDrillingProgramService
{
Task<IEnumerable<FileCategoryDto>> GetCategoriesAsync(CancellationToken token = default);
Task<DrillingProgramStateDto> GetStateAsync(int idWell, int fileChangerId,
CancellationToken token = default);
Task<int> CreateFileMarkAsync(FileMarkDto fileMarkDto, int idUser, CancellationToken token);
Task<int> MarkFileMarkAsDeletedAsync(int idMark, CancellationToken token);
Task<int> AddOrReplaceFileMarkAsync(FileMarkDto fileMarkDto, int idUser, CancellationToken token);
Task<int> MarkAsDeletedFileMarkAsync(int idFileMark, CancellationToken token);
Task<int> AddPartAsync(int idWell, int idFileCategory, CancellationToken token = default);
Task<int> RemovePartAsync(int idWell, int idFileCategory, CancellationToken token = default);
Task<int> AddUserAsync(int idUser, int idPart, int idUserRole, CancellationToken token = default);
Task<int> RemoveUserAsync(int idUser, int idPart, int idUserRole, CancellationToken token = default);
Task<int> AddFile(int idPart, int idUser, string fileFullName, Stream fileStream, CancellationToken token = default);
}
}

View File

@ -28,6 +28,7 @@ namespace AsbCloudApp.Services
CancellationToken token = default);
Task<IEnumerable<FileInfoDto>> GetInfosByCategoryAsync(int idWell, int idCategory, CancellationToken token = default);
Task<int> DeleteAsync(int id, CancellationToken token);
Task<int> DeleteAsync(IEnumerable<int> ids, CancellationToken token);
string GetUrl(FileInfoDto fileInfo);
string GetUrl(int idFile);
string GetUrl(int idWell, int idCategory, int idFile, string dotExtention);
@ -35,5 +36,6 @@ namespace AsbCloudApp.Services
Task<int> MarkFileMarkAsDeletedAsync(int idMark, CancellationToken token);
Task<FileInfoDto> MoveAsync(int idWell, int? idUser, int idCategory, string destinationFileName, string srcFileFullName, CancellationToken token = default);
Task<FileInfoDto> GetByMarkId(int idMark, CancellationToken token);
Task<int> MarkFileMarkAsDeletedAsync(IEnumerable<int> idsMarks, CancellationToken token);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,90 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Add_DrillingProgram_parts : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "t_drilling_program_part",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
id_well = table.Column<int>(type: "integer", nullable: false),
id_file_category = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_t_drilling_program_part", x => x.id);
table.ForeignKey(
name: "FK_t_drilling_program_part_t_file_category_id_file_category",
column: x => x.id_file_category,
principalTable: "t_file_category",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_t_drilling_program_part_t_well_id_well",
column: x => x.id_well,
principalTable: "t_well",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
},
comment: "части программ бурения");
migrationBuilder.CreateTable(
name: "t_relation_user_drilling_program_part",
columns: table => new
{
id_user = table.Column<int>(type: "integer", nullable: false),
id_drilling_program_part = table.Column<int>(type: "integer", nullable: false),
id_role = table.Column<int>(type: "integer", nullable: false, comment: "1 - publisher, 2 - approver")
},
constraints: table =>
{
table.PrimaryKey("t_relation_user_drilling_program_part_pk", x => new { x.id_user, x.id_drilling_program_part });
table.ForeignKey(
name: "FK_t_relation_user_drilling_program_part_t_drilling_program_pa~",
column: x => x.id_drilling_program_part,
principalTable: "t_drilling_program_part",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_t_relation_user_drilling_program_part_t_user_id_user",
column: x => x.id_user,
principalTable: "t_user",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
},
comment: "Отношение пользователей и частей ПБ");
migrationBuilder.CreateIndex(
name: "IX_t_drilling_program_part_id_file_category",
table: "t_drilling_program_part",
column: "id_file_category");
migrationBuilder.CreateIndex(
name: "IX_t_drilling_program_part_id_well",
table: "t_drilling_program_part",
column: "id_well");
migrationBuilder.CreateIndex(
name: "IX_t_relation_user_drilling_program_part_id_drilling_program_p~",
table: "t_relation_user_drilling_program_part",
column: "id_drilling_program_part");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "t_relation_user_drilling_program_part");
migrationBuilder.DropTable(
name: "t_drilling_program_part");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class DrillingProgram_parts_Add_unique_constrain : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_t_drilling_program_part_id_well",
table: "t_drilling_program_part");
migrationBuilder.CreateIndex(
name: "IX_t_drilling_program_part_id_well_id_file_category",
table: "t_drilling_program_part",
columns: new[] { "id_well", "id_file_category" },
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_t_drilling_program_part_id_well_id_file_category",
table: "t_drilling_program_part");
migrationBuilder.CreateIndex(
name: "IX_t_drilling_program_part_id_well",
table: "t_drilling_program_part",
column: "id_well");
}
}
}

View File

@ -266,6 +266,35 @@ namespace AsbCloudDb.Migrations
b.HasComment("Параметры коридоров бурения (диапазоны параметров бурения)");
});
modelBuilder.Entity("AsbCloudDb.Model.DrillingProgramPart", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("IdFileCategory")
.HasColumnType("integer")
.HasColumnName("id_file_category");
b.Property<int>("IdWell")
.HasColumnType("integer")
.HasColumnName("id_well");
b.HasKey("Id");
b.HasIndex("IdFileCategory");
b.HasIndex("IdWell", "IdFileCategory")
.IsUnique();
b.ToTable("t_drilling_program_part");
b.HasComment("части программ бурения");
});
modelBuilder.Entity("AsbCloudDb.Model.DrillParams", b =>
{
b.Property<int>("Id")
@ -1354,6 +1383,31 @@ namespace AsbCloudDb.Migrations
b.HasComment("отношение скважин и компаний");
});
modelBuilder.Entity("AsbCloudDb.Model.RelationUserDrillingProgramPart", b =>
{
b.Property<int>("IdUser")
.HasColumnType("integer")
.HasColumnName("id_user");
b.Property<int>("IdDrillingProgramPart")
.HasColumnType("integer")
.HasColumnName("id_drilling_program_part");
b.Property<int>("IdDrillingProgramPartRole")
.HasColumnType("integer")
.HasColumnName("id_role")
.HasComment("1 - publisher, 2 - approver");
b.HasKey("IdUser", "IdDrillingProgramPart")
.HasName("t_relation_user_drilling_program_part_pk");
b.HasIndex("IdDrillingProgramPart");
b.ToTable("t_relation_user_drilling_program_part");
b.HasComment("Отношение пользователей и частей ПБ");
});
modelBuilder.Entity("AsbCloudDb.Model.RelationUserRolePermission", b =>
{
b.Property<int>("IdUserRole")
@ -4399,6 +4453,25 @@ namespace AsbCloudDb.Migrations
b.Navigation("Well");
});
modelBuilder.Entity("AsbCloudDb.Model.DrillingProgramPart", b =>
{
b.HasOne("AsbCloudDb.Model.FileCategory", "FileCategory")
.WithMany()
.HasForeignKey("IdFileCategory")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("AsbCloudDb.Model.Well", "Well")
.WithMany("DrillingProgramParts")
.HasForeignKey("IdWell")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("FileCategory");
b.Navigation("Well");
});
modelBuilder.Entity("AsbCloudDb.Model.DrillParams", b =>
{
b.HasOne("AsbCloudDb.Model.Well", "Well")
@ -4505,6 +4578,25 @@ namespace AsbCloudDb.Migrations
b.Navigation("Well");
});
modelBuilder.Entity("AsbCloudDb.Model.RelationUserDrillingProgramPart", b =>
{
b.HasOne("AsbCloudDb.Model.DrillingProgramPart", "DrillingProgramPart")
.WithMany("RelatedUsers")
.HasForeignKey("IdDrillingProgramPart")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("AsbCloudDb.Model.User", "User")
.WithMany()
.HasForeignKey("IdUser")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("DrillingProgramPart");
b.Navigation("User");
});
modelBuilder.Entity("AsbCloudDb.Model.RelationUserRolePermission", b =>
{
b.HasOne("AsbCloudDb.Model.Permission", "Permission")
@ -4795,6 +4887,11 @@ namespace AsbCloudDb.Migrations
b.Navigation("Clusters");
});
modelBuilder.Entity("AsbCloudDb.Model.DrillingProgramPart", b =>
{
b.Navigation("RelatedUsers");
});
modelBuilder.Entity("AsbCloudDb.Model.FileInfo", b =>
{
b.Navigation("FileMarks");
@ -4847,6 +4944,8 @@ namespace AsbCloudDb.Migrations
modelBuilder.Entity("AsbCloudDb.Model.Well", b =>
{
b.Navigation("DrillingProgramParts");
b.Navigation("RelationCompaniesWells");
b.Navigation("WellCompositeSrcs");

View File

@ -238,7 +238,12 @@ namespace AsbCloudDb.Model
{
entity.HasIndex(d => d.IdWellOperationCategory);
});
modelBuilder.Entity<DrillingProgramPart>(entity => {
entity.HasIndex(x => new { x.IdWell, x.IdFileCategory})
.IsUnique();
});
modelBuilder.Entity<FileMark>(entity =>
{
entity.HasOne(d => d.User)

View File

@ -13,10 +13,15 @@ namespace AsbCloudDb.Model
[Column("id")]
public int Id { get; set; }
[Column("id_well")]
public int IdWell { get; set; }
[Column("id_file_category")]
[Required]
public int IdFileCategory { get; set; }
[ForeignKey(nameof(IdWell))]
public Well Well { get; set; }
[ForeignKey(nameof(IdFileCategory))]
public FileCategory FileCategory { get; set; }

View File

@ -16,7 +16,7 @@ namespace AsbCloudDb.Model
[Column("id_file"), Comment("id файла")]
public int IdFile { get; set; }
[Column("id_mark_type"), Comment("0 - Согласован")]
[Column("id_mark_type"), Comment("0 - отклонен, 1 - согласован")]
public int IdMarkType { get; set; }
[Column("date_created", TypeName = "timestamp with time zone"), Comment("Дата совершенного действия")]

View File

@ -39,6 +39,8 @@ namespace AsbCloudDb.Model
DbSet<RelationUserRolePermission> RelationUserRolePermissions { get; set; }
DatabaseFacade Database { get; }
DbSet<DrillingProgramPart> DrillingProgramParts { get; set; }
DbSet<RelationUserDrillingProgramPart> RelationDrillingProgramPartUsers { get; set; }
int SaveChanges();
int SaveChanges(bool acceptAllChangesOnSuccess);

View File

@ -13,13 +13,13 @@ namespace AsbCloudDb.Model
public int IdDrillingProgramPart { get; set; }
[Column("id_role"), Comment("1 - publisher, 2 - approver")]
public int IdDrillingProgramPartRole { get; set; }
public int IdUserRole { get; set; }
[ForeignKey(nameof(IdDrillingProgramPart))]
[InverseProperty(nameof(Model.DrillingProgramPart.RelatedUsers))]
public virtual DrillingProgramPart DrillingProgramPart { get; set; }
[ForeignKey(nameof(IdUser))]
[InverseProperty(nameof(Model.User.RelationUsersUserRoles))]
public virtual User User { get; set; }
}

View File

@ -64,5 +64,8 @@ namespace AsbCloudDb.Model
[InverseProperty(nameof(WellComposite.WellSrc))]
public virtual ICollection<WellComposite> WellCompositeSrcs { get; set; }
[InverseProperty(nameof(DrillingProgramPart.Well))]
public virtual ICollection<DrillingProgramPart> DrillingProgramParts { get; set; }
}
}

View File

@ -37,7 +37,6 @@
<ItemGroup>
<Folder Include="CommonLibs\" />
<Folder Include="Services\DrillingProgram\" />
</ItemGroup>
</Project>

View File

@ -13,6 +13,7 @@ using Microsoft.Extensions.DependencyInjection;
using System;
using FluentValidation;
using FluentValidation.AspNetCore;
using AsbCloudInfrastructure.Services.DrillingProgram;
namespace AsbCloudInfrastructure
{

View File

@ -0,0 +1,178 @@
using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Services;
using ClosedXML.Excel;
using ClosedXML.Excel.Drawings;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services.DrillingProgram
{
internal class DrillingProgramMaker
{
private const int maxAllowedColumns = 256;
//public async Task<FileInfoDto> GetOrCreateAsync(int idWell, int idUser, CancellationToken token = default)
//{
// var programParts = (await fileService.GetInfosByCategoryAsync(idWell, idFileCategoryDrillingProgramItems, token)
// .ConfigureAwait(false))
// .Where(f => f.FileMarks?.Any(m => m.IdMarkType == 1 && !m.IsDeleted) ?? false);
// var well = await wellService.GetAsync(idWell, token)
// .ConfigureAwait(false);
// var programs = await fileService.GetInfosByCategoryAsync(idWell, idFileCategoryDrillingProgram, token)
// .ConfigureAwait(false);
// if (programs is not null && programs.Any() && programParts.Any())
// {
// programs = programs.OrderByDescending(f => f.UploadDate);
// var matchFilesIterator = programs.GetEnumerator();
// matchFilesIterator.MoveNext();
// var matchFile = matchFilesIterator.Current;
// if (programParts.All(pp => matchFile.UploadDate > pp.UploadDate) &&
// File.Exists(fileService.GetUrl(matchFile)))
// return matchFile;
// else
// await fileService.DeleteAsync(matchFile.Id, token)
// .ConfigureAwait(false);
// while (matchFilesIterator.MoveNext())
// await fileService.DeleteAsync(matchFilesIterator.Current.Id, token)
// .ConfigureAwait(false);
// }
// if (!programParts.Any())
// throw new FileNotFoundException("Нет частей для формирования программы бурения");
// var resultFileName = $"Программа бурения {well.Cluster} {well.Caption}.xlsx";
// var filteredFilePaths = programParts
// .Select(file => fileService.GetUrl(file));
// var tempResultFilePath = Path.Combine(Path.GetTempPath(), "drillingProgram", resultFileName);
// UniteExcelFiles(filteredFilePaths, tempResultFilePath);
// var fileInfo = await fileService.MoveAsync(idWell, null, idFileCategoryDrillingProgram,
// resultFileName, tempResultFilePath, token).ConfigureAwait(false);
// return fileInfo;
//}
private static void UniteExcelFiles(IEnumerable<string> excelFilesNames, string resultExcelPath)
{
var resultExcelFile = new XLWorkbook(XLEventTracking.Disabled);
var filteredFileNames = excelFilesNames.Distinct();
foreach (var excelFileName in filteredFileNames)
{
using var workbookSrc = new XLWorkbook(excelFileName, XLEventTracking.Disabled);
foreach (var sheet in workbookSrc.Worksheets)
{
if (sheet.Visibility == XLWorksheetVisibility.Visible)
CopySheet(resultExcelFile, sheet);
}
}
resultExcelFile.SaveAs(resultExcelPath,
new SaveOptions { EvaluateFormulasBeforeSaving = true });
}
private static void CopySheet(XLWorkbook workbookDst, IXLWorksheet sheetSrc)
{
var newSheetName = sheetSrc.Name;
var suffix = "";
int index = 1;
while (workbookDst.Worksheets.Contains(newSheetName))
{
newSheetName = sheetSrc.Name;
suffix = $"_{index++}";
if (newSheetName.Length + suffix.Length >= 31)
newSheetName = newSheetName[..(31 - suffix.Length)];
newSheetName += suffix;
}
var imagesInfos = sheetSrc.Pictures.Select(p => new ImageInfo
{
Id = p.Id,
Data = p.ImageStream.GetBuffer(),
Height = p.Height,
Width = p.Width,
TopLeftCellAddress = p.TopLeftCell.Address,
Left = p.Left,
Top = p.Top
}).ToList();
IXLWorksheet resultSheet;
if (sheetSrc.Columns().Count() > maxAllowedColumns)
{
resultSheet = workbookDst.Worksheets.Add(newSheetName);
var rngData = GetCellsRange(sheetSrc);
rngData.CopyTo(resultSheet.Cell(1, 1));
var lastRowWithData = rngData.LastRowUsed().RangeAddress
.LastAddress.RowNumber;
for (int i = 1; i < lastRowWithData; i++)
{
resultSheet.Row(i).Height = sheetSrc.Row(i).Height;
resultSheet.Column(i).Width = sheetSrc.Column(i).Width;
}
}
else
{
RemovePicturesFromSheet(sheetSrc);
resultSheet = sheetSrc.CopyTo(workbookDst, newSheetName);
}
CopyImagesToAnotherSheet(imagesInfos, resultSheet);
}
private static IXLWorksheet CopyImagesToAnotherSheet(IEnumerable<ImageInfo> imagesInfos,
IXLWorksheet resultSheet)
{
foreach (var image in imagesInfos)
{
var stream = new MemoryStream();
stream.Write(image.Data, 0, image.Data.Length);
resultSheet.AddPicture(stream)
.WithPlacement(XLPicturePlacement.Move)
.WithSize(image.Width, image.Height)
.MoveTo(resultSheet.Cell(image.TopLeftCellAddress),
image.Left, image.Top);
}
return resultSheet;
}
private static void RemovePicturesFromSheet(IXLWorksheet sheet)
{
var filteredPics = sheet.Pictures.Select(p => p.Name).Distinct().ToList();
foreach (var n in filteredPics)
sheet.Pictures.Delete(n);
}
private static IXLRange GetCellsRange(IXLWorksheet sheet)
{
var firstTableCell = sheet.FirstCellUsed();
var lastTableCell = sheet.LastCellUsed();
var rngData = sheet.Range(firstTableCell.Address, lastTableCell.Address);
return rngData;
}
}
}

View File

@ -0,0 +1,339 @@
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;
}
}
}

View File

@ -0,0 +1,15 @@
using ClosedXML.Excel;
namespace AsbCloudInfrastructure.Services.DrillingProgram
{
class ImageInfo
{
public int Id { get; set; }
public byte[] Data { get; set; }
public int Height { get; set; }
public int Width { get; set; }
public IXLAddress TopLeftCellAddress { get; set; }
public int Left { get; set; }
public int Top { get; set; }
}
}

View File

@ -1,256 +0,0 @@
using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Services;
using ClosedXML.Excel;
using ClosedXML.Excel.Drawings;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services
{
public class DrillingProgramService : IDrillingProgramService
{
private readonly IFileService fileService;
private readonly IWellService wellService;
private const int idFileCategoryDrillingProgramItems = 13;
private const int idFileCategoryDrillingProgram = 14;
private const int maxAllowedColumns = 256;
public DrillingProgramService(IFileService fileService, IWellService wellService)
{
this.fileService = fileService;
this.wellService = wellService;
}
public async Task<string> GetOrCreateSharedUrlAsync(int idWell, int idUser, IFileShareService fileShareService, CancellationToken token = default)
{
var fileInfo = await GetOrCreateAsync(idWell, idUser, token)
.ConfigureAwait(false);
if (fileInfo is null)
return null;
var sharedUrl = await fileService.GetSharedUrlAsync(fileInfo, idUser, fileShareService, token)
.ConfigureAwait(false);
return sharedUrl;
}
public async Task<FileInfoDto> GetOrCreateAsync(int idWell, int idUser, CancellationToken token = default)
{
var programParts = (await fileService.GetInfosByCategoryAsync(idWell, idFileCategoryDrillingProgramItems, token)
.ConfigureAwait(false))
.Where(f => f.FileMarks?.Any(m => m.IdMarkType == 1 && !m.IsDeleted)??false);
var well = await wellService.GetAsync(idWell, token)
.ConfigureAwait(false);
var programs = await fileService.GetInfosByCategoryAsync(idWell, idFileCategoryDrillingProgram, token)
.ConfigureAwait(false);
if (programs is not null && programs.Any() && programParts.Any())
{
programs = programs.OrderByDescending(f => f.UploadDate);
var matchFilesIterator = programs.GetEnumerator();
matchFilesIterator.MoveNext();
var matchFile = matchFilesIterator.Current;
if (programParts.All(pp => matchFile.UploadDate > pp.UploadDate) &&
File.Exists(fileService.GetUrl(matchFile)))
return matchFile;
else
await fileService.DeleteAsync(matchFile.Id, token)
.ConfigureAwait(false);
while (matchFilesIterator.MoveNext())
await fileService.DeleteAsync(matchFilesIterator.Current.Id, token)
.ConfigureAwait(false);
}
if (!programParts.Any())
throw new FileNotFoundException("Нет частей для формирования программы бурения");
var resultFileName = $"Программа бурения {well.Cluster} {well.Caption}.xlsx";
var filteredFilePaths = programParts
.Select(file => fileService.GetUrl(file));
var tempResultFilePath = Path.Combine(Path.GetTempPath(), "drillingProgram", resultFileName);
UniteExcelFiles(filteredFilePaths, tempResultFilePath);
var fileInfo = await fileService.MoveAsync(idWell, null, idFileCategoryDrillingProgram,
resultFileName, tempResultFilePath, token).ConfigureAwait(false);
return fileInfo;
}
public async Task<int> CreateFileMarkAsync(FileMarkDto fileMarkDto, int idUser, CancellationToken token)
{
var fileInfo = await fileService.GetInfoAsync(fileMarkDto.IdFile, token)
.ConfigureAwait(false);
if (fileInfo.IdCategory != idFileCategoryDrillingProgramItems)
throw new ArgumentInvalidException($"Этот метод допустим только для файлов-частей программы бурения idCategory=={idFileCategoryDrillingProgramItems}.", nameof(fileMarkDto));
var result = await fileService.CreateFileMarkAsync(fileMarkDto, idUser, token)
.ConfigureAwait(false);
var drillingPrograms = await fileService.GetInfosByCategoryAsync(fileInfo.IdWell, idFileCategoryDrillingProgram, token)
.ConfigureAwait(false);
foreach (var drillingProgram in drillingPrograms)
await fileService.DeleteAsync(drillingProgram.Id, token)
.ConfigureAwait(false);
return result;
}
public async Task<int> MarkFileMarkAsDeletedAsync(int idMark,
CancellationToken token)
{
var fileInfo = await fileService.GetByMarkId(idMark, token)
.ConfigureAwait(false);
if (fileInfo.IdCategory != idFileCategoryDrillingProgramItems)
throw new ArgumentInvalidException($"Этот метод допустим только для файлов-частей программы бурения idCategory=={idFileCategoryDrillingProgramItems}.", nameof(idMark));
var result = await fileService.MarkFileMarkAsDeletedAsync(idMark, token)
.ConfigureAwait(false);
var drillingPrograms = await fileService.GetInfosByCategoryAsync(fileInfo.IdWell, idFileCategoryDrillingProgram, token)
.ConfigureAwait(false);
foreach (var drillingProgram in drillingPrograms)
await fileService.DeleteAsync(drillingProgram.Id, token)
.ConfigureAwait(false);
return result;
}
private static void UniteExcelFiles(IEnumerable<string> excelFilesNames, string resultExcelPath)
{
var resultExcelFile = new XLWorkbook(XLEventTracking.Disabled);
var filteredFileNames = excelFilesNames.Distinct();
foreach (var excelFileName in filteredFileNames)
{
using var workbookSrc = new XLWorkbook(excelFileName, XLEventTracking.Disabled);
foreach (var sheet in workbookSrc.Worksheets)
{
if(sheet.Visibility == XLWorksheetVisibility.Visible)
CopySheet(resultExcelFile, sheet);
}
}
resultExcelFile.SaveAs(resultExcelPath,
new SaveOptions { EvaluateFormulasBeforeSaving = true});
}
private static void CopySheet(XLWorkbook workbookDst, IXLWorksheet sheetSrc)
{
var newSheetName = sheetSrc.Name;
var suffix = "";
int index = 1;
while (workbookDst.Worksheets.Contains(newSheetName))
{
newSheetName = sheetSrc.Name;
suffix = $"_{index++}";
if (newSheetName.Length + suffix.Length >= 31)
newSheetName = newSheetName[..(31 - suffix.Length)];
newSheetName += suffix;
}
var imagesInfos = sheetSrc.Pictures.Select(p => new ImageInfo
{
Id = p.Id,
Data = p.ImageStream.GetBuffer(),
Height = p.Height,
Width = p.Width,
TopLeftCellAddress = p.TopLeftCell.Address,
Left = p.Left,
Top = p.Top
}).ToList();
IXLWorksheet resultSheet;
if (sheetSrc.Columns().Count() > maxAllowedColumns)
{
resultSheet = workbookDst.Worksheets.Add(newSheetName);
var rngData = GetCellsRange(sheetSrc);
rngData.CopyTo(resultSheet.Cell(1, 1));
var lastRowWithData = rngData.LastRowUsed().RangeAddress
.LastAddress.RowNumber;
for (int i = 1; i < lastRowWithData; i++)
{
resultSheet.Row(i).Height = sheetSrc.Row(i).Height;
resultSheet.Column(i).Width = sheetSrc.Column(i).Width;
}
}
else
{
RemovePicturesFromSheet(sheetSrc);
resultSheet = sheetSrc.CopyTo(workbookDst, newSheetName);
}
CopyImagesToAnotherSheet(imagesInfos, resultSheet);
}
private static IXLWorksheet CopyImagesToAnotherSheet(IEnumerable<ImageInfo> imagesInfos,
IXLWorksheet resultSheet)
{
foreach (var image in imagesInfos)
{
var stream = new MemoryStream();
stream.Write(image.Data, 0, image.Data.Length);
resultSheet.AddPicture(stream)
.WithPlacement(XLPicturePlacement.Move)
.WithSize(image.Width, image.Height)
.MoveTo(resultSheet.Cell(image.TopLeftCellAddress),
image.Left, image.Top);
}
return resultSheet;
}
private static void RemovePicturesFromSheet(IXLWorksheet sheet)
{
var filteredPics = sheet.Pictures.Select(p => p.Name).Distinct().ToList();
foreach (var n in filteredPics)
sheet.Pictures.Delete(n);
}
private static IXLRange GetCellsRange(IXLWorksheet sheet)
{
var firstTableCell = sheet.FirstCellUsed();
var lastTableCell = sheet.LastCellUsed();
var rngData = sheet.Range(firstTableCell.Address, lastTableCell.Address);
return rngData;
}
}
class ImageInfo
{
public int Id { get; set; }
public byte[] Data { get; set; }
public int Height { get; set; }
public int Width { get; set; }
public IXLAddress TopLeftCellAddress { get; set; }
public int Left { get; set; }
public int Top { get; set; }
}
}

View File

@ -246,20 +246,31 @@ namespace AsbCloudInfrastructure.Services
return await db.SaveChangesAsync(token).ConfigureAwait(false);
}
public async Task<int> DeleteAsync(int idFile, CancellationToken token)
{
var fileInfo = await db.Files
.FirstOrDefaultAsync(f => f.Id == idFile, token)
.ConfigureAwait(false);
public Task<int> DeleteAsync(int idFile, CancellationToken token)
=> DeleteAsync(new int[] { idFile}, token);
if (fileInfo is null)
public async Task<int> DeleteAsync(IEnumerable<int> ids, CancellationToken token)
{
if (ids is null || !ids.Any())
return 0;
var fileName = GetUrl(fileInfo.Adapt<FileInfoDto>());
if (File.Exists(fileName))
File.Delete(fileName);
var filesQuery = db.Files
.Where(f => ids.Contains(f.Id));
var files = await filesQuery.ToListAsync(token);
if (files is null || !files.Any())
return 0;
foreach (var file in files)
{
var fileName = GetUrl(file.IdWell, file.IdCategory, file.Id, Path.GetExtension(file.Name));
if (File.Exists(fileName))
File.Delete(fileName);
}
db.Files.RemoveRange(filesQuery);
db.Files.Remove(fileInfo);
return await db.SaveChangesAsync(token).ConfigureAwait(false);
}
@ -330,16 +341,22 @@ namespace AsbCloudInfrastructure.Services
db.FileMarks.Add(newFileMark);
return await db.SaveChangesAsync(token);
}
public async Task<int> MarkFileMarkAsDeletedAsync(int idMark,
public Task<int> MarkFileMarkAsDeletedAsync(int idMark,
CancellationToken token)
=> MarkFileMarkAsDeletedAsync(new int[] { idMark }, token);
public async Task<int> MarkFileMarkAsDeletedAsync(IEnumerable<int> idsMarks,
CancellationToken token)
{
var fileMark = await db.FileMarks
.FirstOrDefaultAsync(m => m.Id == idMark, token)
.ConfigureAwait(false);
fileMark.IsDeleted = true;
var fileMarkQuery = db.FileMarks
.Where(m => idsMarks.Contains( m.Id ));
foreach (var fileMark in fileMarkQuery)
fileMark.IsDeleted = true;
return await db.SaveChangesAsync(token);
}
}
private async Task<int> SaveWeblinkToFileInfo(int idFileInfo, int idUser, string weblink,
CancellationToken token)

View File

@ -1,6 +1,5 @@
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Services.Cache;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

View File

@ -1,8 +1,12 @@
using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -24,54 +28,30 @@ namespace AsbCloudWebApi.Controllers
this.fileService = fileService;
this.wellService = wellService;
}
/// <summary>
/// Список категорий файлов из которых состоит программа бурения
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet("categories")]
[ProducesResponseType(typeof(FileCategoryDto[]), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetCategories(CancellationToken token)
{
var result = await drillingProgramService.GetCategoriesAsync(token);
return Ok(result);
}
/// <summary>
/// Создает программу бурения
/// Состояние процесса согласования программы бурения
/// </summary>
/// <param name="idWell"> id скважины </param>
/// <param name="token"> Токен отмены задачи </param>
/// <returns> Возвращает файл программы бурения </returns>
[HttpGet]
[Permission]
[ProducesResponseType(typeof(FileResult), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetAsync(int idWell, CancellationToken token = default)
{
var idCompany = User.GetCompanyId();
var fileChangerId = User.GetUserId();
if (idCompany is null || fileChangerId is null ||
!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
idWell, token).ConfigureAwait(false))
return Forbid();
try
{
var fileInfo = await drillingProgramService.GetOrCreateAsync(idWell,
(int)fileChangerId, token)
.ConfigureAwait(false);
var relativePath = fileService.GetUrl(fileInfo);
return PhysicalFile(Path.GetFullPath(relativePath), "application/octet-stream", fileInfo.Name);
}
catch (FileNotFoundException ex)
{
return NotFound(ex.FileName);
}
}
/// <summary>
/// Создает программу бурения
/// </summary>
/// <param name="idWell"> id скважины </param>
/// <param name="fileShareService"></param>
/// <param name="token"> Токен отмены задачи </param>
/// <returns> Возвращает ссылку на файл программы бурения в облаке </returns>
[HttpGet("webUrl")]
[Permission]
[ProducesResponseType(typeof(string), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetOrCreateSharedUrlAsync(int idWell, [FromServices]IFileShareService fileShareService, CancellationToken token = default)
[ProducesResponseType(typeof(DrillingProgramStateDto), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetStateAsync(int idWell, CancellationToken token)
{
var idCompany = User.GetCompanyId();
@ -81,16 +61,143 @@ namespace AsbCloudWebApi.Controllers
!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
idWell, token).ConfigureAwait(false))
return Forbid();
var sharedUrl = await drillingProgramService.GetOrCreateSharedUrlAsync(idWell,
(int)idUser, fileShareService, token)
.ConfigureAwait(false);
return Ok(sharedUrl);
var result = await drillingProgramService.GetStateAsync(idWell, (int)idUser, token);
return Ok(result);
}
/// <summary>
/// Создает метку для файла входящего в программу бурения
/// Загрузка файла программы бурения
/// </summary>
/// <param name="idPart">ID части программы. Не путать с категорией файла</param>
/// <param name="idWell"></param>
/// <param name="files"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpPost("{idPart}")]
[Permission]
[ProducesResponseType(typeof(Task<int>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> AddFile(
int idPart,
int idWell,
[FromForm] IFormFileCollection files,
CancellationToken token)
{
int? idCompany = User.GetCompanyId();
int? idUser = User.GetUserId();
if (idCompany is null || idUser is null)
return Forbid();
if (!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
idWell, token).ConfigureAwait(false))
return Forbid();
if (files.Count > 1)
return BadRequest(ArgumentInvalidException.MakeValidationError(nameof(files), "only 1 file can be uploaded"));
if (files.Count == 0)
return BadRequest(ArgumentInvalidException.MakeValidationError(nameof(files), "at list 1 file can be uploaded"));
var fileName = files[0].FileName;
var fileStream = files[0].OpenReadStream();
var result = await drillingProgramService.AddFile(idPart, (int)idUser, fileName, fileStream, token);
return Ok(result);
}
/// <summary>
/// Добавить раздел программы бурения
/// </summary>
/// <param name="idWell"></param>
/// <param name="idFileCategory"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpPost("part")]
[Permission]
[ProducesResponseType(typeof(Task<int>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> AddPartAsync(int idWell, int idFileCategory, CancellationToken token)
{
int? idCompany = User.GetCompanyId();
int? idUser = User.GetUserId();
if (idCompany is null || idUser is null)
return Forbid();
if (!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
idWell, token).ConfigureAwait(false))
return Forbid();
var result = drillingProgramService.AddPartAsync(idWell, idFileCategory, token);
return Ok(result);
}
/// <summary>
/// Удалить раздел программы бурения
/// </summary>
/// <param name="idWell"></param>
/// <param name="idFileCategory"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpPost("part")]
[Permission]
[ProducesResponseType(typeof(Task<int>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> RemovePartAsync(int idWell, int idFileCategory, CancellationToken token)
{
int? idCompany = User.GetCompanyId();
int? idUser = User.GetUserId();
if (idCompany is null || idUser is null)
return Forbid();
if (!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
idWell, token).ConfigureAwait(false))
return Forbid();
var result = drillingProgramService.RemovePartAsync(idWell, idFileCategory, token);
return Ok(result);
}
[HttpPost("part/{idPart}/user/{idUser}")]
[Permission]
[ProducesResponseType(typeof(Task<int>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> AddUserAsync(int idWell, int idUser, int idPart, int idUserRole, CancellationToken token = default)
{
int? idCompany = User.GetCompanyId();
int? idUserEditor = User.GetUserId();
if (idCompany is null || idUserEditor is null)
return Forbid();
if (!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
idWell, token).ConfigureAwait(false))
return Forbid();
var result = drillingProgramService.AddUserAsync(idUser, idPart, idUserRole, token);
return Ok(result);
}
[HttpDelete("part/{idPart}/user/{idUser}")]
[Permission]
[ProducesResponseType(typeof(Task<int>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> RemoveUserAsync(int idWell, int idUser, int idPart, int idUserRole, CancellationToken token = default)
{
int? idCompany = User.GetCompanyId();
int? idUserEditor = User.GetUserId();
if (idCompany is null || idUserEditor is null)
return Forbid();
if (!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
idWell, token).ConfigureAwait(false))
return Forbid();
var result = drillingProgramService.RemoveUserAsync(idUser, idPart, idUserRole, token);
return Ok(result);
}
/// <summary>
/// Создает метку для файла входящего в программу бурения, заменить существующую
/// </summary>
/// <param name="idWell"> id скважины </param>
/// <param name="markDto">метка файла</param>
@ -98,7 +205,7 @@ namespace AsbCloudWebApi.Controllers
/// <returns></returns>
[HttpPost("fileMark")]
[Permission]
public async Task<IActionResult> CreateFileMarkAsync(int idWell, FileMarkDto markDto, CancellationToken token = default)
public async Task<IActionResult> AddOrReplaceFileMarkAsync(int idWell, FileMarkDto markDto, CancellationToken token)
{
var idCompany = User.GetCompanyId();
@ -109,7 +216,7 @@ namespace AsbCloudWebApi.Controllers
idWell, token).ConfigureAwait(false))
return Forbid();
var result = await drillingProgramService.CreateFileMarkAsync(markDto, (int)idUser, token)
var result = await drillingProgramService.AddOrReplaceFileMarkAsync(markDto, (int)idUser, token)
.ConfigureAwait(false);
return Ok(result);
@ -125,8 +232,8 @@ namespace AsbCloudWebApi.Controllers
[HttpDelete("fileMark")]
[Permission]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> DeleteFileMarkAsync(int idWell, int idMark,
CancellationToken token = default)
public async Task<IActionResult> MarkAsDeletedFileMarkAsync(int idWell, int idMark,
CancellationToken token)
{
var idCompany = User.GetCompanyId();
@ -134,7 +241,7 @@ namespace AsbCloudWebApi.Controllers
idWell, token).ConfigureAwait(false))
return Forbid();
var result = await drillingProgramService.MarkFileMarkAsDeletedAsync(idMark, token)
var result = await drillingProgramService.MarkAsDeletedFileMarkAsync(idMark, token)
.ConfigureAwait(false);
return Ok(result);

View File

@ -53,13 +53,6 @@ namespace AsbCloudWebApi.Controllers
if(!userService.HasPermission((int)idUser, $"File.edit{idCategory}"))
return Forbid();
var fileInfoCollection = files.Select(f => new FileInfoDto
{
Name = f.FileName,
IdCategory = idCategory,
UploadDate = DateTime.Now
});
foreach (var file in files)
{
var fileStream = file.OpenReadStream();

View File

@ -3,7 +3,7 @@ using System.Security.Claims;
namespace AsbCloudWebApi
{
public static class Extensions
public static class Extentions
{
public static int? GetCompanyId(this ClaimsPrincipal user)
{

View File

@ -30,6 +30,12 @@ namespace AsbCloudWebApi.Middlewares
var body = MakeJsonBody(ex);
await context.Response.WriteAsync(body);
}
catch(ForbidException ex)
{
Console.WriteLine($"ForbidException in {context.Request.Method}: {ex.Message}");
context.Response.Clear();
context.Response.StatusCode = 403;
}
catch (TaskCanceledException ex)
{
Console.WriteLine(ex.Message);
@ -45,7 +51,7 @@ namespace AsbCloudWebApi.Middlewares
private static string MakeJsonBody(ArgumentInvalidException ex)
{
object error = new { name = ex.ParamName, errors = new string[] { ex.Message } };
object error = ex.ToValaidationErrorObject();
var buffer = System.Text.Json.JsonSerializer.Serialize(error);
return buffer;
}