forked from ddrilling/AsbCloudServer
Merge pull request 'Faq-контроллер и его бизнес-логика' (#48) from feature/faq into dev
Reviewed-on: http://test.digitaldrilling.ru:8080/DDrilling/AsbCloudServer/pulls/48 Reviewed-by: Никита Фролов <ng.frolov@digitaldrilling.ru>
This commit is contained in:
commit
57d262d85a
56
AsbCloudApp/Data/FaqDto.cs
Normal file
56
AsbCloudApp/Data/FaqDto.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using System;
|
||||
|
||||
namespace AsbCloudApp.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// DTO для faq-вопроса
|
||||
/// </summary>
|
||||
public class FaqDto : IId
|
||||
{
|
||||
/// <summary>
|
||||
/// ключ вопроса
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ключ автора вопроса
|
||||
/// </summary>
|
||||
public int? IdAuthorQuestion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// автор ответа
|
||||
/// </summary>
|
||||
public int? IdAuthorAnswer { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// дата создания вопроса
|
||||
/// </summary>
|
||||
public DateTimeOffset? DateCreatedQuestion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// текст вопроса
|
||||
/// </summary>
|
||||
public string Question { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// текст ответа
|
||||
/// </summary>
|
||||
public string? Answer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// статус вопроса
|
||||
/// </summary>
|
||||
public int State { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Счетчик повторений вопроса
|
||||
/// </summary>
|
||||
public int CounterQuestion { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Частый вопрос
|
||||
/// </summary>
|
||||
public bool IsFrequently { get; set; } = false;
|
||||
}
|
||||
}
|
65
AsbCloudApp/Repositories/IFaqRepository.cs
Normal file
65
AsbCloudApp/Repositories/IFaqRepository.cs
Normal file
@ -0,0 +1,65 @@
|
||||
using AsbCloudApp.Data;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using AsbCloudApp.Requests;
|
||||
using AsbCloudApp.Services;
|
||||
|
||||
namespace AsbCloudApp.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// репозиторий по работе с faq-вопросами
|
||||
/// </summary>
|
||||
public interface IFaqRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Получить список вопросов
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<FaqDto>> GetFilteredAsync(FaqRequest request, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Добавить вопрос
|
||||
/// </summary>
|
||||
/// <param name="faqDto"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> InsertAsync(FaqDto faqDto, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Обновить существующий вопрос
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> UpdateAsync(FaqDto dto, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Объединить 2 вопроса в 1
|
||||
/// </summary>
|
||||
/// <param name="sourceId1">ключ первого вопроса, подлежащего объединению</param>
|
||||
/// <param name="sourceId2">ключ второго вопроса, подлежащего объединению</param>
|
||||
/// <param name="mergeQuestions">Флаг, объединять текст вопросов или нет</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> MergeAsync(int sourceId1, int sourceId2, bool mergeQuestions, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Пометить вопрос по id как удаленный
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> MarkAsDeletedAsync(int id, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Удалить вопрос по ключу
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> DeleteAsync(int id, CancellationToken token);
|
||||
}
|
||||
}
|
30
AsbCloudApp/Requests/FaqRequest.cs
Normal file
30
AsbCloudApp/Requests/FaqRequest.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AsbCloudApp.Requests
|
||||
{
|
||||
/// <summary>
|
||||
/// Параметры запроса для фильтрации faq-вопросов
|
||||
/// </summary>
|
||||
public class FaqRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Есть ли ответ на вопрос
|
||||
/// </summary>
|
||||
public bool HasAnswer { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Частый вопрос
|
||||
/// </summary>
|
||||
public bool IsFrequently { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Включать ли в выборку удаленные вопросы
|
||||
/// </summary>
|
||||
public bool IncludeDeleted { get; set; } = false;
|
||||
}
|
||||
}
|
7951
AsbCloudDb/Migrations/20230418051912_AddTable_t_faq.Designer.cs
generated
Normal file
7951
AsbCloudDb/Migrations/20230418051912_AddTable_t_faq.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
78
AsbCloudDb/Migrations/20230418051912_AddTable_t_faq.cs
Normal file
78
AsbCloudDb/Migrations/20230418051912_AddTable_t_faq.cs
Normal file
@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace AsbCloudDb.Migrations
|
||||
{
|
||||
public partial class AddTable_t_faq : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "t_faq",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "integer", nullable: false, comment: "Идентификатор")
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
id_well = table.Column<int>(type: "integer", nullable: false, comment: "id скважины"),
|
||||
id_author_question = table.Column<int>(type: "integer", nullable: false, comment: "id автора вопроса"),
|
||||
id_author_answer = table.Column<int>(type: "integer", nullable: false, comment: "id автора ответа"),
|
||||
date_created_question = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата создания вопроса"),
|
||||
date_answer = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true, comment: "Дата ответа"),
|
||||
date_last_edited_question = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата последнего редактирования вопроса"),
|
||||
question = table.Column<string>(type: "text", nullable: false, comment: "Текст вопроса"),
|
||||
answer = table.Column<string>(type: "text", nullable: false, comment: "Текст ответа"),
|
||||
state = table.Column<int>(type: "integer", nullable: false, comment: "Статус вопроса"),
|
||||
counter_question = table.Column<int>(type: "integer", nullable: false, comment: "Счетчик повторений вопроса"),
|
||||
id_replacement_question = table.Column<int>(type: "integer", nullable: true, comment: "Ключ заменяющего вопроса"),
|
||||
is_frequently_question = table.Column<bool>(type: "boolean", nullable: false, comment: "Частый вопрос")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_t_faq", x => x.id);
|
||||
table.ForeignKey(
|
||||
name: "FK_t_faq_t_user_id_author_answer",
|
||||
column: x => x.id_author_answer,
|
||||
principalTable: "t_user",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_t_faq_t_user_id_author_question",
|
||||
column: x => x.id_author_question,
|
||||
principalTable: "t_user",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_t_faq_t_well_id_well",
|
||||
column: x => x.id_well,
|
||||
principalTable: "t_well",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
},
|
||||
comment: "вопросы пользователей");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_t_faq_id_author_answer",
|
||||
table: "t_faq",
|
||||
column: "id_author_answer");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_t_faq_id_author_question",
|
||||
table: "t_faq",
|
||||
column: "id_author_question");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_t_faq_id_well",
|
||||
table: "t_faq",
|
||||
column: "id_well");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "t_faq");
|
||||
}
|
||||
}
|
||||
}
|
7949
AsbCloudDb/Migrations/20230418072820_UpdateTable_t_faq_Nullable_IdAuthorAnswer.Designer.cs
generated
Normal file
7949
AsbCloudDb/Migrations/20230418072820_UpdateTable_t_faq_Nullable_IdAuthorAnswer.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,60 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace AsbCloudDb.Migrations
|
||||
{
|
||||
public partial class UpdateTable_t_faq_Nullable_IdAuthorAnswer : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_t_faq_t_user_id_author_answer",
|
||||
table: "t_faq");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "id_author_answer",
|
||||
table: "t_faq",
|
||||
type: "integer",
|
||||
nullable: true,
|
||||
comment: "id автора ответа",
|
||||
oldClrType: typeof(int),
|
||||
oldType: "integer",
|
||||
oldComment: "id автора ответа");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_t_faq_t_user_id_author_answer",
|
||||
table: "t_faq",
|
||||
column: "id_author_answer",
|
||||
principalTable: "t_user",
|
||||
principalColumn: "id");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_t_faq_t_user_id_author_answer",
|
||||
table: "t_faq");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "id_author_answer",
|
||||
table: "t_faq",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
comment: "id автора ответа",
|
||||
oldClrType: typeof(int),
|
||||
oldType: "integer",
|
||||
oldNullable: true,
|
||||
oldComment: "id автора ответа");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_t_faq_t_user_id_author_answer",
|
||||
table: "t_faq",
|
||||
column: "id_author_answer",
|
||||
principalTable: "t_user",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
7948
AsbCloudDb/Migrations/20230418075021_UpdateTable_t_faq_Nullable_Answer.Designer.cs
generated
Normal file
7948
AsbCloudDb/Migrations/20230418075021_UpdateTable_t_faq_Nullable_Answer.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,37 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace AsbCloudDb.Migrations
|
||||
{
|
||||
public partial class UpdateTable_t_faq_Nullable_Answer : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "answer",
|
||||
table: "t_faq",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
comment: "Текст ответа",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldComment: "Текст ответа");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "answer",
|
||||
table: "t_faq",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
comment: "Текст ответа",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true,
|
||||
oldComment: "Текст ответа");
|
||||
}
|
||||
}
|
||||
}
|
7948
AsbCloudDb/Migrations/20230418103709_UpdateTable_t_faq_Field_IsFrequently_Rename.Designer.cs
generated
Normal file
7948
AsbCloudDb/Migrations/20230418103709_UpdateTable_t_faq_Field_IsFrequently_Rename.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,25 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace AsbCloudDb.Migrations
|
||||
{
|
||||
public partial class UpdateTable_t_faq_Field_IsFrequently_Rename : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "is_frequently_question",
|
||||
table: "t_faq",
|
||||
newName: "is_frequently");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "is_frequently",
|
||||
table: "t_faq",
|
||||
newName: "is_frequently_question");
|
||||
}
|
||||
}
|
||||
}
|
8054
AsbCloudDb/Migrations/20230421071633_UpdateTable_t_faq_Id_Well_Remove.Designer.cs
generated
Normal file
8054
AsbCloudDb/Migrations/20230421071633_UpdateTable_t_faq_Id_Well_Remove.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,48 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace AsbCloudDb.Migrations
|
||||
{
|
||||
public partial class UpdateTable_t_faq_Id_Well_Remove : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_t_faq_t_well_id_well",
|
||||
table: "t_faq");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_t_faq_id_well",
|
||||
table: "t_faq");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "id_well",
|
||||
table: "t_faq");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "id_well",
|
||||
table: "t_faq",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
comment: "id скважины");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_t_faq_id_well",
|
||||
table: "t_faq",
|
||||
column: "id_well");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_t_faq_t_well_id_well",
|
||||
table: "t_faq",
|
||||
column: "id_well",
|
||||
principalTable: "t_well",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
@ -333,6 +333,83 @@ namespace AsbCloudDb.Migrations
|
||||
b.HasComment("части программ бурения");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AsbCloudDb.Model.Faq", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id")
|
||||
.HasComment("Идентификатор");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Answer")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("answer")
|
||||
.HasComment("Текст ответа");
|
||||
|
||||
b.Property<int>("CounterQuestion")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("counter_question")
|
||||
.HasComment("Счетчик повторений вопроса");
|
||||
|
||||
b.Property<DateTimeOffset?>("DateAnswer")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("date_answer")
|
||||
.HasComment("Дата ответа");
|
||||
|
||||
b.Property<DateTimeOffset>("DateCreatedQuestion")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("date_created_question")
|
||||
.HasComment("Дата создания вопроса");
|
||||
|
||||
b.Property<DateTimeOffset>("DateLastEditedQuestion")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("date_last_edited_question")
|
||||
.HasComment("Дата последнего редактирования вопроса");
|
||||
|
||||
b.Property<int?>("IdAuthorAnswer")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id_author_answer")
|
||||
.HasComment("id автора ответа");
|
||||
|
||||
b.Property<int>("IdAuthorQuestion")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id_author_question")
|
||||
.HasComment("id автора вопроса");
|
||||
|
||||
b.Property<int?>("IdReplacementQuestion")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id_replacement_question")
|
||||
.HasComment("Ключ заменяющего вопроса");
|
||||
|
||||
b.Property<bool>("IsFrequently")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_frequently")
|
||||
.HasComment("Частый вопрос");
|
||||
|
||||
b.Property<string>("Question")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("question")
|
||||
.HasComment("Текст вопроса");
|
||||
|
||||
b.Property<int>("State")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("state")
|
||||
.HasComment("Статус вопроса");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IdAuthorAnswer");
|
||||
|
||||
b.HasIndex("IdAuthorQuestion");
|
||||
|
||||
b.ToTable("t_faq");
|
||||
|
||||
b.HasComment("вопросы пользователей");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AsbCloudDb.Model.FileCategory", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@ -7206,6 +7283,23 @@ namespace AsbCloudDb.Migrations
|
||||
b.Navigation("Well");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AsbCloudDb.Model.Faq", b =>
|
||||
{
|
||||
b.HasOne("AsbCloudDb.Model.User", "AuthorAnswer")
|
||||
.WithMany()
|
||||
.HasForeignKey("IdAuthorAnswer");
|
||||
|
||||
b.HasOne("AsbCloudDb.Model.User", "AuthorQuestion")
|
||||
.WithMany()
|
||||
.HasForeignKey("IdAuthorQuestion")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("AuthorAnswer");
|
||||
|
||||
b.Navigation("AuthorQuestion");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AsbCloudDb.Model.FileInfo", b =>
|
||||
{
|
||||
b.HasOne("AsbCloudDb.Model.User", "Author")
|
||||
|
@ -72,6 +72,8 @@ namespace AsbCloudDb.Model
|
||||
private static int referenceCount;
|
||||
public static int ReferenceCount => referenceCount;
|
||||
|
||||
public DbSet<Faq> Faqs => Set<Faq>();
|
||||
|
||||
public AsbCloudDbContext() : base()
|
||||
{
|
||||
Interlocked.Increment(ref referenceCount);
|
||||
|
94
AsbCloudDb/Model/Faq.cs
Normal file
94
AsbCloudDb/Model/Faq.cs
Normal file
@ -0,0 +1,94 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AsbCloudDb.Model
|
||||
{
|
||||
[Table("t_faq"), Comment("вопросы пользователей")]
|
||||
public class Faq : IId
|
||||
{
|
||||
public const int StateOpened = 0;
|
||||
public const int StateDeleted = 1;
|
||||
|
||||
[Key]
|
||||
[Column("id"), Comment("Идентификатор")]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// автор вопроса
|
||||
/// </summary>
|
||||
[Column("id_author_question"), Comment("id автора вопроса")]
|
||||
public int IdAuthorQuestion { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
[ForeignKey(nameof(IdAuthorQuestion))]
|
||||
public virtual User AuthorQuestion { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// автор ответа
|
||||
/// </summary>
|
||||
[Column("id_author_answer"), Comment("id автора ответа")]
|
||||
public int? IdAuthorAnswer { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
[ForeignKey(nameof(IdAuthorAnswer))]
|
||||
public virtual User? AuthorAnswer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// дата создания вопроса
|
||||
/// </summary>
|
||||
[Column("date_created_question"), Comment("Дата создания вопроса")]
|
||||
public DateTimeOffset DateCreatedQuestion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// дата ответа
|
||||
/// </summary>
|
||||
[Column("date_answer"), Comment("Дата ответа")]
|
||||
public DateTimeOffset? DateAnswer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// дата последнего редактирования вопроса
|
||||
/// </summary>
|
||||
[Column("date_last_edited_question"), Comment("Дата последнего редактирования вопроса")]
|
||||
public DateTimeOffset DateLastEditedQuestion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// текст вопроса
|
||||
/// </summary>
|
||||
[Column("question"), Comment("Текст вопроса")]
|
||||
public string Question { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// текст ответа
|
||||
/// </summary>
|
||||
[Column("answer"), Comment("Текст ответа")]
|
||||
public string? Answer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// статус вопроса
|
||||
/// </summary>
|
||||
[Column("state"), Comment("Статус вопроса")]
|
||||
public int State { get; set; } = Faq.StateOpened;
|
||||
|
||||
/// <summary>
|
||||
/// Счетчик повторений вопроса
|
||||
/// </summary>
|
||||
[Column("counter_question"), Comment("Счетчик повторений вопроса")]
|
||||
public int CounterQuestion { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Ключ заменяющего вопроса
|
||||
/// </summary>
|
||||
[Column("id_replacement_question"), Comment("Ключ заменяющего вопроса")]
|
||||
public int? IdReplacementQuestion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Частый вопрос
|
||||
/// </summary>
|
||||
[Column("is_frequently"), Comment("Частый вопрос")]
|
||||
public bool IsFrequently { get; set; } = false;
|
||||
|
||||
}
|
||||
}
|
@ -57,6 +57,7 @@ namespace AsbCloudDb.Model
|
||||
DbSet<WellFinalDocument> WellFinalDocuments { get; }
|
||||
DbSet<LimitingParameter> LimitingParameter { get; }
|
||||
DbSet<TelemetryWirelineRunOut> TelemetryWirelineRunOut { get; }
|
||||
DbSet<Faq> Faqs { get; }
|
||||
|
||||
DbSet<Record1> Record1 { get; }
|
||||
DbSet<Record7> Record7 { get; }
|
||||
|
@ -180,6 +180,7 @@ namespace AsbCloudInfrastructure
|
||||
services.AddTransient<ITelemetryWirelineRunOutRepository, TelemetryWirelineRunOutRepository>();
|
||||
services.AddTransient<IWellFinalDocumentsRepository, WellFinalDocumentsRepository>();
|
||||
services.AddTransient<IPlannedTrajectoryRepository, PlannedTrajectoryRepository>();
|
||||
services.AddTransient<IFaqRepository, FaqRepository>();
|
||||
|
||||
// Subsystem service
|
||||
services.AddTransient<ICrudRepository<SubsystemDto>, CrudCacheRepositoryBase<SubsystemDto, Subsystem>>();
|
||||
|
154
AsbCloudInfrastructure/Repository/FaqRepository.cs
Normal file
154
AsbCloudInfrastructure/Repository/FaqRepository.cs
Normal file
@ -0,0 +1,154 @@
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Exceptions;
|
||||
using AsbCloudApp.Repositories;
|
||||
using AsbCloudApp.Requests;
|
||||
using AsbCloudApp.Services;
|
||||
using AsbCloudDb;
|
||||
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.Repository
|
||||
{
|
||||
/// <summary>
|
||||
/// сервис по работе с faq-вопросами
|
||||
/// </summary>
|
||||
public class FaqRepository : IFaqRepository
|
||||
{
|
||||
private readonly IAsbCloudDbContext db;
|
||||
private readonly IWellService wellService;
|
||||
|
||||
public FaqRepository(IAsbCloudDbContext db, IWellService wellService)
|
||||
{
|
||||
this.db = db;
|
||||
this.wellService = wellService;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<FaqDto>> GetFilteredAsync(FaqRequest request, CancellationToken token)
|
||||
{
|
||||
var query = db.Faqs.AsNoTracking();
|
||||
|
||||
if (request.IsFrequently)
|
||||
{
|
||||
query = query.Where(o => o.IsFrequently);
|
||||
}
|
||||
if (request.HasAnswer)
|
||||
{
|
||||
query = query.Where(o => !string.IsNullOrEmpty(o.Answer));
|
||||
}
|
||||
if (!request.IncludeDeleted)
|
||||
{
|
||||
query = query.Where(o => o.State != Faq.StateDeleted);
|
||||
}
|
||||
|
||||
var entities = query
|
||||
.OrderByDescending(e => e.DateCreatedQuestion)
|
||||
.Select(o => new FaqDto()
|
||||
{
|
||||
Id = o.Id,
|
||||
Question = o.Question,
|
||||
Answer = o.Answer,
|
||||
CounterQuestion = o.CounterQuestion,
|
||||
IsFrequently = o.IsFrequently,
|
||||
State = o.State,
|
||||
DateCreatedQuestion = o.DateCreatedQuestion,
|
||||
});
|
||||
|
||||
|
||||
var result = await entities.AsNoTracking()
|
||||
.ToArrayAsync(token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
public async Task<int> InsertAsync(FaqDto faqDto, CancellationToken token)
|
||||
{
|
||||
var entity = faqDto.Adapt<Faq>();
|
||||
entity.DateCreatedQuestion = entity.DateLastEditedQuestion = DateTimeOffset.UtcNow;
|
||||
entity.CounterQuestion = 1;
|
||||
entity.State = Faq.StateOpened;
|
||||
|
||||
db.Faqs.Add(entity);
|
||||
|
||||
await db.SaveChangesAsync(token).ConfigureAwait(false);
|
||||
return entity.Id;
|
||||
}
|
||||
|
||||
public async Task<int> MergeAsync(int sourceId1, int sourceId2, bool mergeQuestions, CancellationToken token)
|
||||
{
|
||||
var sourceFaqs = db.Faqs.Where(e => e.Id == sourceId1 || e.Id == sourceId2).ToArray();
|
||||
if (sourceFaqs.Count() < 2)
|
||||
throw new ArgumentInvalidException("Questions with target ids don't exist", nameof(sourceFaqs));
|
||||
|
||||
var newFaq = new Faq()
|
||||
{
|
||||
CounterQuestion = sourceFaqs.Sum(e => e.CounterQuestion),
|
||||
State = 0,
|
||||
IsFrequently = sourceFaqs.Any(e => e.IsFrequently),
|
||||
IdAuthorQuestion = sourceFaqs.Last().IdAuthorQuestion,
|
||||
DateCreatedQuestion = DateTimeOffset.UtcNow,
|
||||
DateLastEditedQuestion = DateTimeOffset.UtcNow,
|
||||
Answer = mergeQuestions == true
|
||||
? string.Join(Environment.NewLine, sourceFaqs.Select(e => e.Answer))
|
||||
: sourceFaqs.Last().Answer,
|
||||
Question = mergeQuestions == true
|
||||
? string.Join(Environment.NewLine, sourceFaqs.Select(e => e.Question))
|
||||
: sourceFaqs.Last().Question,
|
||||
};
|
||||
|
||||
db.Faqs.Add(newFaq);
|
||||
await db.SaveChangesAsync(token).ConfigureAwait(false);
|
||||
|
||||
if (newFaq.Id == 0)
|
||||
throw new ArgumentInvalidException("Error creating new question", nameof(newFaq));
|
||||
|
||||
foreach (var sourceFaq in sourceFaqs)
|
||||
{
|
||||
sourceFaq.IdReplacementQuestion = newFaq.Id;
|
||||
sourceFaq.State = Faq.StateDeleted;
|
||||
}
|
||||
await db.SaveChangesAsync(token).ConfigureAwait(false);
|
||||
|
||||
return newFaq.Id;
|
||||
}
|
||||
|
||||
public async Task<int> UpdateAsync(FaqDto dto, CancellationToken token)
|
||||
{
|
||||
var entity = dto.Adapt<Faq>();
|
||||
entity.DateLastEditedQuestion = DateTimeOffset.UtcNow;
|
||||
db.Faqs.Update(entity);
|
||||
return await db.SaveChangesAsync(token)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<int> MarkAsDeletedAsync(int id, CancellationToken token)
|
||||
{
|
||||
var entity = db.Faqs.FirstOrDefault(e => e.Id == id);
|
||||
if (entity is null)
|
||||
throw new ArgumentInvalidException("Question doesn't exist", nameof(id));
|
||||
|
||||
entity.State = Faq.StateDeleted;
|
||||
entity.DateLastEditedQuestion = DateTimeOffset.UtcNow;
|
||||
|
||||
return await db.SaveChangesAsync(token)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<int> DeleteAsync(int id, CancellationToken token)
|
||||
{
|
||||
var faq = db.Faqs.FirstOrDefault(f => f.Id == id);
|
||||
if (faq is null)
|
||||
throw new ArgumentInvalidException("Question doesn't exist", nameof(id));
|
||||
|
||||
db.Faqs.Remove(faq);
|
||||
return await db.SaveChangesAsync(token);
|
||||
}
|
||||
}
|
||||
}
|
@ -161,7 +161,7 @@ namespace AsbCloudInfrastructure.Services.ProcessMap
|
||||
.SetVal(dataParam.Limit);
|
||||
|
||||
sheet.Cell(row, column + columnOffsetPercent)
|
||||
.SetVal(dataParam.SetpointUsage);
|
||||
.SetVal(dataParam.SetpointUsage, format: "0.0");
|
||||
}
|
||||
|
||||
private static void FillIntervalModeDataSpeed(IXLWorksheet sheet, ProcessMapReportParamsDto dataParam, int column, int row)
|
||||
@ -185,7 +185,7 @@ namespace AsbCloudInfrastructure.Services.ProcessMap
|
||||
.SetVal(dataParam.Limit);
|
||||
|
||||
sheet.Cell(row, column + columnOffsetPercent)
|
||||
.SetVal(dataParam.SetpointUsage);
|
||||
.SetVal(dataParam.SetpointUsage, format: "0.0");
|
||||
}
|
||||
|
||||
private static Stream GetExcelTemplateStream()
|
||||
|
133
AsbCloudWebApi/Controllers/FaqController.cs
Normal file
133
AsbCloudWebApi/Controllers/FaqController.cs
Normal file
@ -0,0 +1,133 @@
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Exceptions;
|
||||
using AsbCloudApp.Repositories;
|
||||
using AsbCloudApp.Requests;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AsbCloudWebApi.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// контроллер по работе с faq-вопросами
|
||||
/// </summary>
|
||||
[Route("api/faq")]
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
public class FaqController : ControllerBase
|
||||
{
|
||||
private readonly IFaqRepository faqRepository;
|
||||
|
||||
public FaqController(IFaqRepository faqRepository)
|
||||
{
|
||||
this.faqRepository = faqRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// получение списка faq-вопросов
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[Permission]
|
||||
[ProducesResponseType(typeof(IEnumerable<FaqDto>), (int)System.Net.HttpStatusCode.OK)]
|
||||
public async Task<IActionResult> GetFilteredAsync(
|
||||
[FromQuery] FaqRequest request,
|
||||
CancellationToken token)
|
||||
{
|
||||
var result = await faqRepository.GetFilteredAsync(request, token).ConfigureAwait(false);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// добавление нового вопроса
|
||||
/// </summary>
|
||||
/// <param name="faqDto">вопрос</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[Permission]
|
||||
[ProducesResponseType(typeof(FaqDto), (int)System.Net.HttpStatusCode.OK)]
|
||||
public async Task<IActionResult> InsertAsync(
|
||||
[FromBody] FaqDto faqDto,
|
||||
CancellationToken token)
|
||||
{
|
||||
faqDto.IdAuthorQuestion = User.GetUserId()!.Value;
|
||||
|
||||
var result = await faqRepository.InsertAsync(faqDto, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
[HttpPut]
|
||||
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
|
||||
public async Task<IActionResult> UpdateAsync([FromBody] FaqDto faqDto, CancellationToken token)
|
||||
{
|
||||
faqDto.IdAuthorAnswer = User.GetUserId()!.Value;
|
||||
|
||||
var result = await faqRepository.UpdateAsync(faqDto, token)
|
||||
.ConfigureAwait(false);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Объединение 2 вопросов в один
|
||||
/// </summary>
|
||||
/// <param name="sourceId1"></param>
|
||||
/// <param name="sourceId2"></param>
|
||||
/// <param name="mergeQuestions">настройка, объединять ли текст 2-х вопросов в один или нет</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentInvalidException"></exception>
|
||||
[HttpGet("merge")]
|
||||
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
|
||||
public async Task<IActionResult> MergeAsync(
|
||||
[FromQuery] int sourceId1,
|
||||
[FromQuery] int sourceId2,
|
||||
[FromQuery] bool mergeQuestions,
|
||||
CancellationToken token)
|
||||
{
|
||||
var result = await faqRepository.MergeAsync(sourceId1, sourceId2, mergeQuestions, token)
|
||||
.ConfigureAwait(false);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Помечает вопрос как удаленный
|
||||
/// </summary>
|
||||
/// <param name="id">id вопроса</param>
|
||||
/// <param name="token">Токен отмены задачи</param>
|
||||
/// <returns>Количество удаленных из БД строк</returns>
|
||||
[HttpPut("{id}")]
|
||||
[Permission]
|
||||
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
|
||||
public async Task<IActionResult> MarkAsDeletedAsync(int id, CancellationToken token)
|
||||
{
|
||||
var result = await faqRepository.MarkAsDeletedAsync(id, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Удаление вопроса
|
||||
/// </summary>
|
||||
/// <param name="id">id вопроса</param>
|
||||
/// <param name="token">Токен отмены задачи</param>
|
||||
/// <returns>Количество удаленных из БД строк</returns>
|
||||
[HttpDelete("{id}")]
|
||||
[Permission]
|
||||
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
|
||||
public async Task<IActionResult> DeleteAsync(int id, CancellationToken token)
|
||||
{
|
||||
var result = await faqRepository.DeleteAsync(id, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user