forked from ddrilling/AsbCloudServer
DrillingProgramService существенно переработан.
This commit is contained in:
parent
4c68045398
commit
e4e906c8d7
@ -5,8 +5,14 @@ namespace AsbCloudApp.Data
|
|||||||
public class DrillingProgramPartDto
|
public class DrillingProgramPartDto
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Description { get; set; }
|
|
||||||
public int IdFileCategory { 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> Publishers { get; set; }
|
||||||
public IEnumerable<UserDto> Approvers { get; set; }
|
public IEnumerable<UserDto> Approvers { get; set; }
|
||||||
public bool PermissionToApprove { get; set; }
|
public bool PermissionToApprove { get; set; }
|
||||||
|
15
AsbCloudApp/Data/FileCategoryDto.cs
Normal file
15
AsbCloudApp/Data/FileCategoryDto.cs
Normal 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; }
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,10 @@ namespace AsbCloudApp.Data
|
|||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public int IdFile { get; set; }
|
public int IdFile { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 0 - отклонен
|
||||||
|
/// 1 - согласован
|
||||||
|
/// </summary>
|
||||||
public int IdMarkType { get; set; }
|
public int IdMarkType { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// äàòà/âðåìÿ äîáàâëåíèÿ.
|
/// äàòà/âðåìÿ äîáàâëåíèÿ.
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
namespace AsbCloudApp.Exceptions
|
namespace AsbCloudApp.Exceptions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Argument validation fail Exception
|
||||||
|
/// </summary>
|
||||||
public class ArgumentInvalidException: Exception
|
public class ArgumentInvalidException: Exception
|
||||||
{
|
{
|
||||||
public string ParamName { get; }
|
public string ParamName { get; }
|
||||||
@ -11,5 +14,15 @@ namespace AsbCloudApp.Exceptions
|
|||||||
{
|
{
|
||||||
ParamName = paramName;
|
ParamName = paramName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public object ToValaidationErrorObject()
|
||||||
|
=> MakeValidationError(ParamName, Message);
|
||||||
|
|
||||||
|
public static object MakeValidationError(string paramName, params string[] errors)
|
||||||
|
=> new
|
||||||
|
{
|
||||||
|
name = paramName,
|
||||||
|
errors = errors,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
17
AsbCloudApp/Exceptions/ForbidException.cs
Normal file
17
AsbCloudApp/Exceptions/ForbidException.cs
Normal 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){}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
using AsbCloudApp.Data;
|
using AsbCloudApp.Data;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -6,9 +8,15 @@ namespace AsbCloudApp.Services
|
|||||||
{
|
{
|
||||||
public interface IDrillingProgramService
|
public interface IDrillingProgramService
|
||||||
{
|
{
|
||||||
|
Task<IEnumerable<FileCategoryDto>> GetCategoriesAsync(CancellationToken token = default);
|
||||||
Task<DrillingProgramStateDto> GetStateAsync(int idWell, int fileChangerId,
|
Task<DrillingProgramStateDto> GetStateAsync(int idWell, int fileChangerId,
|
||||||
CancellationToken token = default);
|
CancellationToken token = default);
|
||||||
Task<int> CreateFileMarkAsync(FileMarkDto fileMarkDto, int idUser, CancellationToken token);
|
Task<int> AddOrReplaceFileMarkAsync(FileMarkDto fileMarkDto, int idUser, CancellationToken token);
|
||||||
Task<int> MarkFileMarkAsDeletedAsync(int idMark, 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -28,6 +28,7 @@ namespace AsbCloudApp.Services
|
|||||||
CancellationToken token = default);
|
CancellationToken token = default);
|
||||||
Task<IEnumerable<FileInfoDto>> GetInfosByCategoryAsync(int idWell, int idCategory, CancellationToken token = default);
|
Task<IEnumerable<FileInfoDto>> GetInfosByCategoryAsync(int idWell, int idCategory, CancellationToken token = default);
|
||||||
Task<int> DeleteAsync(int id, CancellationToken token);
|
Task<int> DeleteAsync(int id, CancellationToken token);
|
||||||
|
Task<int> DeleteAsync(IEnumerable<int> ids, CancellationToken token);
|
||||||
string GetUrl(FileInfoDto fileInfo);
|
string GetUrl(FileInfoDto fileInfo);
|
||||||
string GetUrl(int idFile);
|
string GetUrl(int idFile);
|
||||||
string GetUrl(int idWell, int idCategory, int idFile, string dotExtention);
|
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<int> MarkFileMarkAsDeletedAsync(int idMark, CancellationToken token);
|
||||||
Task<FileInfoDto> MoveAsync(int idWell, int? idUser, int idCategory, string destinationFileName, string srcFileFullName, CancellationToken token = default);
|
Task<FileInfoDto> MoveAsync(int idWell, int? idUser, int idCategory, string destinationFileName, string srcFileFullName, CancellationToken token = default);
|
||||||
Task<FileInfoDto> GetByMarkId(int idMark, CancellationToken token);
|
Task<FileInfoDto> GetByMarkId(int idMark, CancellationToken token);
|
||||||
|
Task<int> MarkFileMarkAsDeletedAsync(IEnumerable<int> idsMarks, CancellationToken token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4980
AsbCloudDb/Migrations/20220209093528_Add_DrillingProgram_parts.Designer.cs
generated
Normal file
4980
AsbCloudDb/Migrations/20220209093528_Add_DrillingProgram_parts.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4981
AsbCloudDb/Migrations/20220209102754_DrillingProgram_parts_Add_unique_constrain.Designer.cs
generated
Normal file
4981
AsbCloudDb/Migrations/20220209102754_DrillingProgram_parts_Add_unique_constrain.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -266,6 +266,35 @@ namespace AsbCloudDb.Migrations
|
|||||||
b.HasComment("Параметры коридоров бурения (диапазоны параметров бурения)");
|
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 =>
|
modelBuilder.Entity("AsbCloudDb.Model.DrillParams", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@ -1354,6 +1383,31 @@ namespace AsbCloudDb.Migrations
|
|||||||
b.HasComment("отношение скважин и компаний");
|
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 =>
|
modelBuilder.Entity("AsbCloudDb.Model.RelationUserRolePermission", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("IdUserRole")
|
b.Property<int>("IdUserRole")
|
||||||
@ -4399,6 +4453,25 @@ namespace AsbCloudDb.Migrations
|
|||||||
b.Navigation("Well");
|
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 =>
|
modelBuilder.Entity("AsbCloudDb.Model.DrillParams", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("AsbCloudDb.Model.Well", "Well")
|
b.HasOne("AsbCloudDb.Model.Well", "Well")
|
||||||
@ -4505,6 +4578,25 @@ namespace AsbCloudDb.Migrations
|
|||||||
b.Navigation("Well");
|
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 =>
|
modelBuilder.Entity("AsbCloudDb.Model.RelationUserRolePermission", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("AsbCloudDb.Model.Permission", "Permission")
|
b.HasOne("AsbCloudDb.Model.Permission", "Permission")
|
||||||
@ -4795,6 +4887,11 @@ namespace AsbCloudDb.Migrations
|
|||||||
b.Navigation("Clusters");
|
b.Navigation("Clusters");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("AsbCloudDb.Model.DrillingProgramPart", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("RelatedUsers");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("AsbCloudDb.Model.FileInfo", b =>
|
modelBuilder.Entity("AsbCloudDb.Model.FileInfo", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("FileMarks");
|
b.Navigation("FileMarks");
|
||||||
@ -4847,6 +4944,8 @@ namespace AsbCloudDb.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("AsbCloudDb.Model.Well", b =>
|
modelBuilder.Entity("AsbCloudDb.Model.Well", b =>
|
||||||
{
|
{
|
||||||
|
b.Navigation("DrillingProgramParts");
|
||||||
|
|
||||||
b.Navigation("RelationCompaniesWells");
|
b.Navigation("RelationCompaniesWells");
|
||||||
|
|
||||||
b.Navigation("WellCompositeSrcs");
|
b.Navigation("WellCompositeSrcs");
|
||||||
|
@ -238,7 +238,12 @@ namespace AsbCloudDb.Model
|
|||||||
{
|
{
|
||||||
entity.HasIndex(d => d.IdWellOperationCategory);
|
entity.HasIndex(d => d.IdWellOperationCategory);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<DrillingProgramPart>(entity => {
|
||||||
|
entity.HasIndex(x => new { x.IdWell, x.IdFileCategory})
|
||||||
|
.IsUnique();
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity<FileMark>(entity =>
|
modelBuilder.Entity<FileMark>(entity =>
|
||||||
{
|
{
|
||||||
entity.HasOne(d => d.User)
|
entity.HasOne(d => d.User)
|
||||||
|
@ -13,10 +13,15 @@ namespace AsbCloudDb.Model
|
|||||||
[Column("id")]
|
[Column("id")]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Column("id_well")]
|
||||||
|
public int IdWell { get; set; }
|
||||||
|
|
||||||
[Column("id_file_category")]
|
[Column("id_file_category")]
|
||||||
[Required]
|
|
||||||
public int IdFileCategory { get; set; }
|
public int IdFileCategory { get; set; }
|
||||||
|
|
||||||
|
[ForeignKey(nameof(IdWell))]
|
||||||
|
public Well Well { get; set; }
|
||||||
|
|
||||||
[ForeignKey(nameof(IdFileCategory))]
|
[ForeignKey(nameof(IdFileCategory))]
|
||||||
public FileCategory FileCategory { get; set; }
|
public FileCategory FileCategory { get; set; }
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ namespace AsbCloudDb.Model
|
|||||||
[Column("id_file"), Comment("id файла")]
|
[Column("id_file"), Comment("id файла")]
|
||||||
public int IdFile { get; set; }
|
public int IdFile { get; set; }
|
||||||
|
|
||||||
[Column("id_mark_type"), Comment("0 - Согласован")]
|
[Column("id_mark_type"), Comment("0 - отклонен, 1 - согласован")]
|
||||||
public int IdMarkType { get; set; }
|
public int IdMarkType { get; set; }
|
||||||
|
|
||||||
[Column("date_created", TypeName = "timestamp with time zone"), Comment("Дата совершенного действия")]
|
[Column("date_created", TypeName = "timestamp with time zone"), Comment("Дата совершенного действия")]
|
||||||
|
@ -39,6 +39,8 @@ namespace AsbCloudDb.Model
|
|||||||
DbSet<RelationUserRolePermission> RelationUserRolePermissions { get; set; }
|
DbSet<RelationUserRolePermission> RelationUserRolePermissions { get; set; }
|
||||||
|
|
||||||
DatabaseFacade Database { get; }
|
DatabaseFacade Database { get; }
|
||||||
|
DbSet<DrillingProgramPart> DrillingProgramParts { get; set; }
|
||||||
|
DbSet<RelationUserDrillingProgramPart> RelationDrillingProgramPartUsers { get; set; }
|
||||||
|
|
||||||
int SaveChanges();
|
int SaveChanges();
|
||||||
int SaveChanges(bool acceptAllChangesOnSuccess);
|
int SaveChanges(bool acceptAllChangesOnSuccess);
|
||||||
|
@ -13,13 +13,13 @@ namespace AsbCloudDb.Model
|
|||||||
public int IdDrillingProgramPart { get; set; }
|
public int IdDrillingProgramPart { get; set; }
|
||||||
|
|
||||||
[Column("id_role"), Comment("1 - publisher, 2 - approver")]
|
[Column("id_role"), Comment("1 - publisher, 2 - approver")]
|
||||||
public int IdDrillingProgramPartRole { get; set; }
|
public int IdUserRole { get; set; }
|
||||||
|
|
||||||
[ForeignKey(nameof(IdDrillingProgramPart))]
|
[ForeignKey(nameof(IdDrillingProgramPart))]
|
||||||
|
[InverseProperty(nameof(Model.DrillingProgramPart.RelatedUsers))]
|
||||||
public virtual DrillingProgramPart DrillingProgramPart { get; set; }
|
public virtual DrillingProgramPart DrillingProgramPart { get; set; }
|
||||||
|
|
||||||
[ForeignKey(nameof(IdUser))]
|
[ForeignKey(nameof(IdUser))]
|
||||||
[InverseProperty(nameof(Model.User.RelationUsersUserRoles))]
|
|
||||||
public virtual User User { get; set; }
|
public virtual User User { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -64,5 +64,8 @@ namespace AsbCloudDb.Model
|
|||||||
|
|
||||||
[InverseProperty(nameof(WellComposite.WellSrc))]
|
[InverseProperty(nameof(WellComposite.WellSrc))]
|
||||||
public virtual ICollection<WellComposite> WellCompositeSrcs { get; set; }
|
public virtual ICollection<WellComposite> WellCompositeSrcs { get; set; }
|
||||||
|
|
||||||
|
[InverseProperty(nameof(DrillingProgramPart.Well))]
|
||||||
|
public virtual ICollection<DrillingProgramPart> DrillingProgramParts { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="CommonLibs\" />
|
<Folder Include="CommonLibs\" />
|
||||||
<Folder Include="Services\DrillingProgram\" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -13,6 +13,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using System;
|
using System;
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using FluentValidation.AspNetCore;
|
using FluentValidation.AspNetCore;
|
||||||
|
using AsbCloudInfrastructure.Services.DrillingProgram;
|
||||||
|
|
||||||
namespace AsbCloudInfrastructure
|
namespace AsbCloudInfrastructure
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
15
AsbCloudInfrastructure/Services/DrillingProgram/ImageInfo.cs
Normal file
15
AsbCloudInfrastructure/Services/DrillingProgram/ImageInfo.cs
Normal 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; }
|
||||||
|
}
|
||||||
|
}
|
@ -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; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -246,20 +246,31 @@ namespace AsbCloudInfrastructure.Services
|
|||||||
return await db.SaveChangesAsync(token).ConfigureAwait(false);
|
return await db.SaveChangesAsync(token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<int> DeleteAsync(int idFile, CancellationToken token)
|
public Task<int> DeleteAsync(int idFile, CancellationToken token)
|
||||||
{
|
=> DeleteAsync(new int[] { idFile}, token);
|
||||||
var fileInfo = await db.Files
|
|
||||||
.FirstOrDefaultAsync(f => f.Id == idFile, token)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (fileInfo is null)
|
public async Task<int> DeleteAsync(IEnumerable<int> ids, CancellationToken token)
|
||||||
|
{
|
||||||
|
if (ids is null || !ids.Any())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
var fileName = GetUrl(fileInfo.Adapt<FileInfoDto>());
|
var filesQuery = db.Files
|
||||||
if (File.Exists(fileName))
|
.Where(f => ids.Contains(f.Id));
|
||||||
File.Delete(fileName);
|
|
||||||
|
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);
|
return await db.SaveChangesAsync(token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,16 +341,22 @@ namespace AsbCloudInfrastructure.Services
|
|||||||
db.FileMarks.Add(newFileMark);
|
db.FileMarks.Add(newFileMark);
|
||||||
return await db.SaveChangesAsync(token);
|
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)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
var fileMark = await db.FileMarks
|
var fileMarkQuery = db.FileMarks
|
||||||
.FirstOrDefaultAsync(m => m.Id == idMark, token)
|
.Where(m => idsMarks.Contains( m.Id ));
|
||||||
.ConfigureAwait(false);
|
|
||||||
fileMark.IsDeleted = true;
|
foreach (var fileMark in fileMarkQuery)
|
||||||
|
fileMark.IsDeleted = true;
|
||||||
|
|
||||||
return await db.SaveChangesAsync(token);
|
return await db.SaveChangesAsync(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> SaveWeblinkToFileInfo(int idFileInfo, int idUser, string weblink,
|
private async Task<int> SaveWeblinkToFileInfo(int idFileInfo, int idUser, string weblink,
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using AsbCloudApp.Services;
|
using AsbCloudApp.Services;
|
||||||
using AsbCloudDb.Model;
|
using AsbCloudDb.Model;
|
||||||
using AsbCloudInfrastructure.Services.Cache;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
using AsbCloudApp.Data;
|
using AsbCloudApp.Data;
|
||||||
|
using AsbCloudApp.Exceptions;
|
||||||
using AsbCloudApp.Services;
|
using AsbCloudApp.Services;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -24,54 +28,30 @@ namespace AsbCloudWebApi.Controllers
|
|||||||
this.fileService = fileService;
|
this.fileService = fileService;
|
||||||
this.wellService = wellService;
|
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>
|
||||||
/// Создает программу бурения
|
/// Состояние процесса согласования программы бурения
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="idWell"> id скважины </param>
|
/// <param name="idWell"> id скважины </param>
|
||||||
/// <param name="token"> Токен отмены задачи </param>
|
/// <param name="token"> Токен отмены задачи </param>
|
||||||
/// <returns> Возвращает файл программы бурения </returns>
|
/// <returns> Возвращает файл программы бурения </returns>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Permission]
|
[Permission]
|
||||||
[ProducesResponseType(typeof(FileResult), (int)System.Net.HttpStatusCode.OK)]
|
[ProducesResponseType(typeof(DrillingProgramStateDto), (int)System.Net.HttpStatusCode.OK)]
|
||||||
public async Task<IActionResult> GetAsync(int idWell, CancellationToken token = default)
|
public async Task<IActionResult> GetStateAsync(int idWell, CancellationToken token)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
var idCompany = User.GetCompanyId();
|
var idCompany = User.GetCompanyId();
|
||||||
|
|
||||||
@ -81,16 +61,143 @@ namespace AsbCloudWebApi.Controllers
|
|||||||
!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
|
!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
|
||||||
idWell, token).ConfigureAwait(false))
|
idWell, token).ConfigureAwait(false))
|
||||||
return Forbid();
|
return Forbid();
|
||||||
|
|
||||||
var sharedUrl = await drillingProgramService.GetOrCreateSharedUrlAsync(idWell,
|
var result = await drillingProgramService.GetStateAsync(idWell, (int)idUser, token);
|
||||||
(int)idUser, fileShareService, token)
|
return Ok(result);
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
return Ok(sharedUrl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="idWell"> id скважины </param>
|
/// <param name="idWell"> id скважины </param>
|
||||||
/// <param name="markDto">метка файла</param>
|
/// <param name="markDto">метка файла</param>
|
||||||
@ -98,7 +205,7 @@ namespace AsbCloudWebApi.Controllers
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost("fileMark")]
|
[HttpPost("fileMark")]
|
||||||
[Permission]
|
[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();
|
var idCompany = User.GetCompanyId();
|
||||||
|
|
||||||
@ -109,7 +216,7 @@ namespace AsbCloudWebApi.Controllers
|
|||||||
idWell, token).ConfigureAwait(false))
|
idWell, token).ConfigureAwait(false))
|
||||||
return Forbid();
|
return Forbid();
|
||||||
|
|
||||||
var result = await drillingProgramService.CreateFileMarkAsync(markDto, (int)idUser, token)
|
var result = await drillingProgramService.AddOrReplaceFileMarkAsync(markDto, (int)idUser, token)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
@ -125,8 +232,8 @@ namespace AsbCloudWebApi.Controllers
|
|||||||
[HttpDelete("fileMark")]
|
[HttpDelete("fileMark")]
|
||||||
[Permission]
|
[Permission]
|
||||||
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
|
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
|
||||||
public async Task<IActionResult> DeleteFileMarkAsync(int idWell, int idMark,
|
public async Task<IActionResult> MarkAsDeletedFileMarkAsync(int idWell, int idMark,
|
||||||
CancellationToken token = default)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
var idCompany = User.GetCompanyId();
|
var idCompany = User.GetCompanyId();
|
||||||
|
|
||||||
@ -134,7 +241,7 @@ namespace AsbCloudWebApi.Controllers
|
|||||||
idWell, token).ConfigureAwait(false))
|
idWell, token).ConfigureAwait(false))
|
||||||
return Forbid();
|
return Forbid();
|
||||||
|
|
||||||
var result = await drillingProgramService.MarkFileMarkAsDeletedAsync(idMark, token)
|
var result = await drillingProgramService.MarkAsDeletedFileMarkAsync(idMark, token)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
|
@ -53,13 +53,6 @@ namespace AsbCloudWebApi.Controllers
|
|||||||
if(!userService.HasPermission((int)idUser, $"File.edit{idCategory}"))
|
if(!userService.HasPermission((int)idUser, $"File.edit{idCategory}"))
|
||||||
return Forbid();
|
return Forbid();
|
||||||
|
|
||||||
var fileInfoCollection = files.Select(f => new FileInfoDto
|
|
||||||
{
|
|
||||||
Name = f.FileName,
|
|
||||||
IdCategory = idCategory,
|
|
||||||
UploadDate = DateTime.Now
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
var fileStream = file.OpenReadStream();
|
var fileStream = file.OpenReadStream();
|
||||||
|
@ -3,7 +3,7 @@ using System.Security.Claims;
|
|||||||
|
|
||||||
namespace AsbCloudWebApi
|
namespace AsbCloudWebApi
|
||||||
{
|
{
|
||||||
public static class Extensions
|
public static class Extentions
|
||||||
{
|
{
|
||||||
public static int? GetCompanyId(this ClaimsPrincipal user)
|
public static int? GetCompanyId(this ClaimsPrincipal user)
|
||||||
{
|
{
|
@ -30,6 +30,12 @@ namespace AsbCloudWebApi.Middlewares
|
|||||||
var body = MakeJsonBody(ex);
|
var body = MakeJsonBody(ex);
|
||||||
await context.Response.WriteAsync(body);
|
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)
|
catch (TaskCanceledException ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine(ex.Message);
|
Console.WriteLine(ex.Message);
|
||||||
@ -45,7 +51,7 @@ namespace AsbCloudWebApi.Middlewares
|
|||||||
|
|
||||||
private static string MakeJsonBody(ArgumentInvalidException ex)
|
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);
|
var buffer = System.Text.Json.JsonSerializer.Serialize(error);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user