FAQ controller и бизнес-логика по работе с вопросами

This commit is contained in:
Olga Nemt 2023-04-20 15:01:19 +05:00
parent d1b8ca7e4b
commit 860da5c35e
18 changed files with 32713 additions and 0 deletions

View File

@ -0,0 +1,63 @@
using System;
namespace AsbCloudApp.Data
{
#nullable enable
/// <summary>
/// DTO для faq-вопроса
/// </summary>
public class FaqDto : IId
{
/// <summary>
/// ключ вопроса
/// </summary>
public int Id { get; set; }
/// <summary>
/// ключ скважины
/// </summary>
public int? IdWell { 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; } = 0!;
/// <summary>
/// Частый вопрос
/// </summary>
public bool? IsFrequently { get; set; } = false!;
}
#nullable disable
}

View File

@ -0,0 +1,59 @@
using AsbCloudApp.Data;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
namespace AsbCloudApp.Repositories
{
#nullable enable
/// <summary>
/// репозиторий по работе с faq-вопросами
/// </summary>
public interface IFaqRepository
{
/// <summary>
/// Получить список вопросов
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<FaqDto>> GetAsync(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="sourceIds">Ключи вопросов, подлежащих объединению</param>
/// <param name="mergeTexts">Флаг, объединять текст вопросов или нет</param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> MergeAsync(IEnumerable<int> sourceIds, bool mergeTexts, CancellationToken token);
/// <summary>
/// Пометить вопрос по id как удаленный
/// </summary>
/// <param name="idWell"></param>
/// <param name="id"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> MarkAsDeletedAsync(int idWell, int id, CancellationToken token);
}
#nullable disable
}

View File

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AsbCloudApp.Requests
{
#nullable enable
/// <summary>
/// Параметры запроса для фильтрации faq-вопросов
/// </summary>
public class FaqRequestBase : RequestBase
{
/// <summary>
/// Есть ли ответ на вопрос
/// </summary>
public bool HasAnswer { get; set; } = false;
/// <summary>
/// Частый вопрос
/// </summary>
public bool IsFrequently { get; set; } = false;
/// <summary>
/// Включать ли в выборку удаленные вопросы
/// </summary>
public bool IncludeDeleted { get; set; } = false;
}
/// <summary>
/// Параметры запроса для фильтрации faq-вопросов (с id скважины)
/// </summary>
public class FaqRequest : FaqRequestBase
{
/// <summary>
/// копирующий конструктор
/// </summary>
/// <param name="request"></param>
/// <param name="idWell"></param>
public FaqRequest(FaqRequestBase request, int idWell)
{
IdWell = idWell;
this.IncludeDeleted = request.IncludeDeleted;
this.HasAnswer = request.HasAnswer;
this.IsFrequently = request.IsFrequently;
}
/// <summary>
/// id скважины
/// </summary>
public int IdWell { get; set; }
}
#nullable disable
}

File diff suppressed because it is too large Load Diff

View 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");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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: "Текст ответа");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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");
}
}
}

View File

@ -333,6 +333,90 @@ 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<int>("IdWell")
.HasColumnType("integer")
.HasColumnName("id_well")
.HasComment("id скважины");
b.Property<bool>("IsFrequently")
.HasColumnType("boolean")
.HasColumnName("is_frequently")
.HasComment("Частый вопрос");
b.Property<int>("State")
.HasColumnType("integer")
.HasColumnName("state")
.HasComment("Статус вопроса");
b.Property<string>("Text")
.IsRequired()
.HasColumnType("text")
.HasColumnName("question")
.HasComment("Текст вопроса");
b.HasKey("Id");
b.HasIndex("IdAuthorAnswer");
b.HasIndex("IdAuthorQuestion");
b.HasIndex("IdWell");
b.ToTable("t_faq");
b.HasComment("вопросы пользователей");
});
modelBuilder.Entity("AsbCloudDb.Model.FileCategory", b =>
{
b.Property<int>("Id")
@ -7118,6 +7202,31 @@ 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.HasOne("AsbCloudDb.Model.Well", "Well")
.WithMany()
.HasForeignKey("IdWell")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("AuthorAnswer");
b.Navigation("AuthorQuestion");
b.Navigation("Well");
});
modelBuilder.Entity("AsbCloudDb.Model.FileInfo", b =>
{
b.HasOne("AsbCloudDb.Model.User", "Author")

View File

@ -66,6 +66,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);

106
AsbCloudDb/Model/Faq.cs Normal file
View File

@ -0,0 +1,106 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
namespace AsbCloudDb.Model
{
#nullable enable
[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_well"), Comment("id скважины")]
public int IdWell { get; set; }
[JsonIgnore]
[ForeignKey(nameof(IdWell))]
public virtual Well Well { get; set; } = null!;
/// <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; } = null!;
/// <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; }
/// <summary>
/// Счетчик повторений вопроса
/// </summary>
[Column("counter_question"), Comment("Счетчик повторений вопроса")]
public int CounterQuestion { get; set; }
/// <summary>
/// Ключ заменяющего вопроса
/// </summary>
[Column("id_replacement_question"), Comment("Ключ заменяющего вопроса")]
public int? IdReplacementQuestion { get; set; }
/// <summary>
/// Частый вопрос
/// </summary>
[Column("is_frequently"), Comment("Частый вопрос")]
public bool IsFrequently { get; set; } = false;
}
#nullable disable
}

View File

@ -53,6 +53,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; }

View File

@ -177,6 +177,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>>();

View File

@ -0,0 +1,159 @@
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
{
#nullable enable
/// <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>> GetAsync(FaqRequest request, CancellationToken token)
{
var query = db.FAQs
.Where(o => o.IdWell == request.IdWell);
if (request.IsFrequently)
{
query = query.Where(o => o.IsFrequently == true);
}
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.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,
});
if (request.SortFields?.Any() == true)
{
entities = entities.SortBy(request.SortFields);
}
else
{
entities = entities.OrderByDescending(e => e.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 = 0;
entity.State = Faq.StateOpened;
db.FAQs.Add(entity);
await db.SaveChangesAsync(token).ConfigureAwait(false);
return entity.Id;
}
public async Task<int> MergeAsync(IEnumerable<int> sourceIds, bool mergeTexts, CancellationToken token)
{
var sourceFaqs = db.FAQs.Where(e => sourceIds.Contains(e.Id)).ToArray();
if (sourceFaqs.Count() < 2)
throw new ArgumentInvalidException("Questions with target ids don't exist", nameof(sourceIds));
if (sourceFaqs.Select(e => e.IdWell).Distinct().Count() > 1)
throw new ArgumentInvalidException("Questions with different idWell are not merged", nameof(sourceIds));
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,
IdWell = sourceFaqs.Last().IdWell,
Answer = mergeTexts == true
? string.Join(Environment.NewLine, sourceFaqs.Select(e => e.Answer))
: sourceFaqs.Last().Answer,
Question = mergeTexts == 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 idWell, 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));
if (entity.IdWell != idWell)
throw new ArgumentInvalidException("Question's well and idWell don't match", nameof(idWell));
entity.State = Faq.StateDeleted;
entity.DateLastEditedQuestion = DateTimeOffset.UtcNow;
return await db.SaveChangesAsync(token)
.ConfigureAwait(false);
}
}
#nullable disable
}

View File

@ -0,0 +1,160 @@
using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers
{
#nullable enable
/// <summary>
/// контроллер по работе с faq-вопросами
/// </summary>
[Route("api/well/{idWell}/faq")]
[ApiController]
[Authorize]
public class FaqController : ControllerBase
{
private readonly IFaqRepository faqRepository;
private readonly IWellService wellService;
public FaqController(IFaqRepository faqRepository, IWellService wellService)
{
this.faqRepository = faqRepository;
this.wellService = wellService;
}
/// <summary>
/// получение списка faq-вопросов
/// </summary>
/// <param name="idWell"></param>
/// <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> GetAsync(
[FromRoute] int idWell,
[FromQuery] FaqRequestBase request,
CancellationToken token)
{
if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false))
return Forbid();
var requestToRepository = new FaqRequest(request, idWell);
var result = await faqRepository.GetAsync(
requestToRepository,
token)
.ConfigureAwait(false);
return Ok(result);
}
/// <summary>
/// добавление нового вопроса
/// </summary>
/// <param name="idWell">ключ скважины</param>
/// <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(
[Range(1, int.MaxValue, ErrorMessage = "Id скважины не может быть меньше 1")] int idWell,
[FromBody] FaqDto faqDto,
CancellationToken token)
{
if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false))
return Forbid();
faqDto.IdWell = idWell;
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(int idWell, [FromBody] FaqDto faqDto, CancellationToken token)
{
if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false))
return Forbid();
faqDto.IdAuthorAnswer = User.GetUserId()!.Value;
var result = await faqRepository.UpdateAsync(faqDto, token)
.ConfigureAwait(false);
return Ok(result);
}
/// <summary>
/// Объединение 2 вопросов в один
/// </summary>
/// <param name="idWell">ключ скважины</param>
/// <param name="sourceIds">ключи вопросов, подлежащих объединению</param>
/// <param name="mergeTexts">настройка, объединять ли текст 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(
int idWell,
[FromQuery] IEnumerable<int> sourceIds,
[FromQuery] bool mergeTexts,
CancellationToken token)
{
if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false))
return Forbid();
if (sourceIds.Count() < 2)
throw new ArgumentInvalidException("Count of questions must be more than 1", nameof(sourceIds));
var result = await faqRepository.MergeAsync(sourceIds, mergeTexts, token)
.ConfigureAwait(false);
return Ok(result);
}
/// <summary>
/// Удаляет вопрос
/// </summary>
/// <param name="idWell">id скважины</param>
/// <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 idWell, int id, CancellationToken token)
{
if (!await CanUserAccessToWellAsync(idWell,
token).ConfigureAwait(false))
return Forbid();
var result = await faqRepository.MarkAsDeletedAsync(idWell, id, token)
.ConfigureAwait(false);
return Ok(result);
}
private async Task<bool> CanUserAccessToWellAsync(int idWell, CancellationToken token)
{
int? idCompany = User.GetCompanyId();
return idCompany is not null && await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
idWell, token).ConfigureAwait(false);
}
}
#nullable disable
}