Доработки

1. Добавлен шаблон суточного отчёта
2. Рефакторинг DTO для суточного отчёта
3. Обновлена валидация входных данных в методах контроллера
4. Небольшой рефакторинг сервисов
This commit is contained in:
Степанов Дмитрий 2023-11-07 15:57:15 +05:00
parent af5d713fc7
commit dcaec8b4a2
10 changed files with 62 additions and 45 deletions

View File

@ -6,11 +6,9 @@ namespace AsbCloudApp.Data.DailyReport.Blocks;
public class ProcessMapWellDrillingRecordDto public class ProcessMapWellDrillingRecordDto
{ {
/// <summary> /// <summary>
/// Id режима бурения /// Режим бурения
/// 1 - ротор
/// 2 - слайд
/// </summary> /// </summary>
public int IdMode { get; set; } public string DrillingMode { get; set; } = null!;
/// <summary> /// <summary>
/// Мех. скорость /// Мех. скорость

View File

@ -8,15 +8,9 @@ namespace AsbCloudApp.Data.DailyReport.Blocks.Subsystems;
public class SubsystemRecordDto public class SubsystemRecordDto
{ {
/// <summary> /// <summary>
/// 1 - АПД, ч/м /// Название подсистемы
/// 11 - АПД ротор
/// 12 - АПД слайд
/// 65536 - Осцилляция
/// 65537 - Демпфер
/// 100000 - Автопроработка
/// 100001 - АвтоСПО
/// </summary> /// </summary>
public int IdSubsystem { get; set; } public string SubsystemName { get; set; } = null!;
/// <summary> /// <summary>
/// Идентификатор временного интервала /// Идентификатор временного интервала

View File

@ -15,6 +15,11 @@ public class TimeBalanceBlockDto : EditableBlock
[Range(1, int.MaxValue)] [Range(1, int.MaxValue)]
public int IdSection { get; set; } public int IdSection { get; set; }
/// <summary>
/// Название секции
/// </summary>
public string? SectionName { get; set; }
/// <summary> /// <summary>
/// Плановая проходка скважины /// Плановая проходка скважины
/// </summary> /// </summary>

View File

@ -6,10 +6,10 @@ namespace AsbCloudApp.Data.DailyReport.Blocks.TimeBalance;
public class TimeBalanceRecordDto public class TimeBalanceRecordDto
{ {
/// <summary> /// <summary>
/// Мех. бурение - 4001 /// Мех. бурение - 1
/// Статический замер - 4002 /// Снятие замера, ориентирование - 2
/// Наращивание - 4004 /// Наращивание, выход на режим - 3
/// Промывка, ОБР - 4012 /// Промывка, проработка - 4
/// </summary> /// </summary>
public int IdWellOperation { get; set; } public int IdWellOperation { get; set; }

View File

@ -6,9 +6,9 @@ namespace AsbCloudApp.Data.DailyReport.Blocks.WellOperation;
public class WellOperationRecordDto public class WellOperationRecordDto
{ {
/// <summary> /// <summary>
/// Id категории операции /// Название категории
/// </summary> /// </summary>
public int? IdWellCategory { get; set; } public string? CategoryName { get; set; }
/// <summary> /// <summary>
/// Продолжительность операции /// Продолжительность операции

View File

@ -43,7 +43,7 @@ public class DailyReportExportService : IDailyReportExportService
private const int columnUseSubsystemPerWellSumDepthInterval = 7; private const int columnUseSubsystemPerWellSumDepthInterval = 7;
private const int columnUseSubsystemPerWellKUsage = 8; private const int columnUseSubsystemPerWellKUsage = 8;
private const int columnProcessMapWellDrillingBlockMode = 2; private const int columnProcessMapWellDrillingBlockDrillingMode = 2;
private const int columnProcessMapWellDrillingBlockWellBoreDepth = 3; private const int columnProcessMapWellDrillingBlockWellBoreDepth = 3;
private const int columnProcessMapWellDrillingBlockMechDrillingHours = 4; private const int columnProcessMapWellDrillingBlockMechDrillingHours = 4;
private const int columnProcessMapWellDrillingBlockRopPlan = 5; private const int columnProcessMapWellDrillingBlockRopPlan = 5;
@ -95,12 +95,12 @@ public class DailyReportExportService : IDailyReportExportService
var stream = await GenerateFileAsync(dailyReport, cancellationToken); var stream = await GenerateFileAsync(dailyReport, cancellationToken);
var fileName = $"Суточный_отчёт_по_скважине_{dailyReport.WellName}_куст_{dailyReport.Cluster}_от_{dailyReport.DateStart:yy-MM-dd}.xlsx"; var fileName = $"Суточный_рапорт_по_скважине_{dailyReport.WellName}_куст_{dailyReport.Cluster}_от_{dailyReport.DateStart:yy-MM-dd}.xlsx";
return (fileName, stream); return (fileName, stream);
} }
private async Task<Stream> GenerateFileAsync(DailyReportDto dailyReport, CancellationToken cancellationToken) private static async Task<Stream> GenerateFileAsync(DailyReportDto dailyReport, CancellationToken cancellationToken)
{ {
using var excelTemplateStream = await Assembly using var excelTemplateStream = await Assembly
.GetExecutingAssembly() .GetExecutingAssembly()
@ -172,7 +172,7 @@ public class DailyReportExportService : IDailyReportExportService
rowCurrent++; rowCurrent++;
} }
sheet.Cell(cellTimeBalanceBlockSection).Value = timeBalanceBlock.IdSection; sheet.Cell(cellTimeBalanceBlockSection).Value = timeBalanceBlock.SectionName;
sheet.Cell(cellTimeBalanceBlockWellDepthPlan).Value = timeBalanceBlock.WellDepthPlan; sheet.Cell(cellTimeBalanceBlockWellDepthPlan).Value = timeBalanceBlock.WellDepthPlan;
sheet.Cell(cellTimeBalanceBlockWellDepthFact).Value = timeBalanceBlock.WellDepthFact; sheet.Cell(cellTimeBalanceBlockWellDepthFact).Value = timeBalanceBlock.WellDepthFact;
sheet.Cell(cellTimeBalanceBlockCountWellOperationSlipsTime).Value = timeBalanceBlock.CountWellOperationSlipsTime; sheet.Cell(cellTimeBalanceBlockCountWellOperationSlipsTime).Value = timeBalanceBlock.CountWellOperationSlipsTime;
@ -180,8 +180,8 @@ public class DailyReportExportService : IDailyReportExportService
private static void AddSubsystemBlockToSheet(IXLWorksheet sheet, SubsystemBlockDto subsystemBlock) private static void AddSubsystemBlockToSheet(IXLWorksheet sheet, SubsystemBlockDto subsystemBlock)
{ {
var groupedModules = subsystemBlock.Modules.OrderBy(m => m.IdSubsystem) var groupedModules = subsystemBlock.Modules.OrderBy(m => m.SubsystemName)
.GroupBy(m => m.IdSubsystem); .GroupBy(m => m.SubsystemName);
var rowСurrent = rowStartSubsystemBlock; var rowСurrent = rowStartSubsystemBlock;
@ -228,9 +228,9 @@ public class DailyReportExportService : IDailyReportExportService
{ {
var rowCurrent = rowStartProcessMapWellDrillingBlock; var rowCurrent = rowStartProcessMapWellDrillingBlock;
foreach (var processMapWellDrilling in processMapWellDrillingBlock.OrderBy(p => p.IdMode)) foreach (var processMapWellDrilling in processMapWellDrillingBlock.OrderBy(p => p.DrillingMode))
{ {
sheet.Cell(rowCurrent, columnProcessMapWellDrillingBlockMode).Value = processMapWellDrilling.IdMode; sheet.Cell(rowCurrent, columnProcessMapWellDrillingBlockDrillingMode).Value = processMapWellDrilling.DrillingMode;
sheet.Cell(rowCurrent, columnProcessMapWellDrillingBlockWellBoreDepth).Value = processMapWellDrilling.WellBoreDepth; sheet.Cell(rowCurrent, columnProcessMapWellDrillingBlockWellBoreDepth).Value = processMapWellDrilling.WellBoreDepth;
sheet.Cell(rowCurrent, columnProcessMapWellDrillingBlockMechDrillingHours).Value = processMapWellDrilling.MechDrillingHours; sheet.Cell(rowCurrent, columnProcessMapWellDrillingBlockMechDrillingHours).Value = processMapWellDrilling.MechDrillingHours;
sheet.Cell(rowCurrent, columnProcessMapWellDrillingBlockRopPlan).Value = processMapWellDrilling.Rop.Plan; sheet.Cell(rowCurrent, columnProcessMapWellDrillingBlockRopPlan).Value = processMapWellDrilling.Rop.Plan;
@ -244,9 +244,9 @@ public class DailyReportExportService : IDailyReportExportService
{ {
sheet.Cell(cellDurationHoursDrillingPerSection).Value = factWellOperationBlock.DurationHoursDrillingPerSection; sheet.Cell(cellDurationHoursDrillingPerSection).Value = factWellOperationBlock.DurationHoursDrillingPerSection;
foreach (var factOperation in factWellOperationBlock.WellOperations.OrderBy(w => w.IdWellCategory)) foreach (var factOperation in factWellOperationBlock.WellOperations.OrderBy(w => w.CategoryName))
{ {
sheet.Cell(rowStartFactWellOperationBlock, columnWellOperationCategory).Value = factOperation.IdWellCategory; sheet.Cell(rowStartFactWellOperationBlock, columnWellOperationCategory).Value = factOperation.CategoryName;
sheet.Cell(rowStartFactWellOperationBlock, columnWellOperationDurationHours).Value = factOperation.DurationHours; sheet.Cell(rowStartFactWellOperationBlock, columnWellOperationDurationHours).Value = factOperation.DurationHours;
} }
} }

View File

@ -115,8 +115,12 @@ public class DailyReportService : IDailyReportService
dailyReport.Deposit = well.Deposit; dailyReport.Deposit = well.Deposit;
dailyReport.Customer = well.Companies.FirstOrDefault(c => c.IdCompanyType == 1)?.Caption; dailyReport.Customer = well.Companies.FirstOrDefault(c => c.IdCompanyType == 1)?.Caption;
dailyReport.Contractor = well.Companies.FirstOrDefault(c => c.IdCompanyType == 2)?.Caption; dailyReport.Contractor = well.Companies.FirstOrDefault(c => c.IdCompanyType == 2)?.Caption;
if (factWellOperations.Any())
{
dailyReport.DepthStart = factWellOperations.Min(o => o.DepthStart); dailyReport.DepthStart = factWellOperations.Min(o => o.DepthStart);
dailyReport.DepthEnd = factWellOperations.Max(o => o.DepthEnd); dailyReport.DepthEnd = factWellOperations.Max(o => o.DepthEnd);
}
await UpdateTimeBalanceBlockAsync(dailyReport, factWellOperations, cancellationToken); await UpdateTimeBalanceBlockAsync(dailyReport, factWellOperations, cancellationToken);
await UpdateSubsystemBlockAsync(dailyReport, cancellationToken); await UpdateSubsystemBlockAsync(dailyReport, cancellationToken);
@ -239,6 +243,9 @@ public class DailyReportService : IDailyReportService
if (dailyReport.TimeBalanceBlock is not null) if (dailyReport.TimeBalanceBlock is not null)
{ {
dailyReport.TimeBalanceBlock.SectionName = wellOperationRepository.GetSectionTypes()
.FirstOrDefault(s => s.Id == dailyReport.TimeBalanceBlock.IdSection)?.Caption;
dailyReport.TimeBalanceBlock.CountWellOperationSlipsTime = (await detectedOperationService.GetAsync( dailyReport.TimeBalanceBlock.CountWellOperationSlipsTime = (await detectedOperationService.GetAsync(
new DetectedOperationRequest new DetectedOperationRequest
{ {
@ -333,10 +340,10 @@ public class DailyReportService : IDailyReportService
cancellationToken)).Where(p => p.DateStart >= dailyReport.DateStart && cancellationToken)).Where(p => p.DateStart >= dailyReport.DateStart &&
p.DateStart <= dailyReport.DateEnd && p.DateStart <= dailyReport.DateEnd &&
p.IdMode.HasValue) p.IdMode.HasValue)
.GroupBy(p => p.IdMode) .GroupBy(p => p.DrillingMode)
.Select(g => new ProcessMapWellDrillingRecordDto .Select(g => new ProcessMapWellDrillingRecordDto
{ {
IdMode = g.Key!.Value, DrillingMode = g.Key,
WellBoreDepth = g.Sum(p => p.DeltaDepth), WellBoreDepth = g.Sum(p => p.DeltaDepth),
Rop = new PlanFactDto<double?> Rop = new PlanFactDto<double?>
{ {
@ -356,7 +363,7 @@ public class DailyReportService : IDailyReportService
WellOperations = factWellOperations.GroupBy(o => o.IdParentCategory) WellOperations = factWellOperations.GroupBy(o => o.IdParentCategory)
.Select(g => new WellOperationRecordDto .Select(g => new WellOperationRecordDto
{ {
IdWellCategory = g.Key, CategoryName = g.First().CategoryName,
DurationHours = g.Sum(o => o.DurationHours) DurationHours = g.Sum(o => o.DurationHours)
}), }),

View File

@ -110,7 +110,7 @@ public class DailyReportServiceTest
{ {
new SubsystemRecordDto new SubsystemRecordDto
{ {
IdSubsystem = 10000, SubsystemName = "АвтоСПО",
IdTimeInterval = 1, IdTimeInterval = 1,
UsedTimeHours = 24, UsedTimeHours = 24,
SumDepthInterval = 1500, SumDepthInterval = 1500,

View File

@ -70,6 +70,13 @@ public class DailyReportController : ControllerBase
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> InsertAsync(int idWell, DateOnly dateStart, CancellationToken cancellationToken) public async Task<IActionResult> InsertAsync(int idWell, DateOnly dateStart, CancellationToken cancellationToken)
{ {
var dateStartToDateTime = dateStart.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc);
var datesRange = await dailyReportService.GetDatesRangeAsync(idWell, cancellationToken);
if (dateStartToDateTime < datesRange?.From || dateStartToDateTime > datesRange?.To)
throw new ArgumentInvalidException("Невозможно сформировать суточный отчёт", nameof(dateStart));
await AssertUserAccessToWell(idWell, cancellationToken); await AssertUserAccessToWell(idWell, cancellationToken);
var id = await dailyReportService.InsertAsync(idWell, dateStart.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc), var id = await dailyReportService.InsertAsync(idWell, dateStart.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc),
@ -90,8 +97,7 @@ public class DailyReportController : ControllerBase
[Permission] [Permission]
[ProducesResponseType(typeof(int), StatusCodes.Status200OK)] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
public Task<IActionResult> public Task<IActionResult> UpdateSignBlockAsync(int idWell, int idDailyReport, SignBlockDto signBlock, CancellationToken cancellationToken) =>
UpdateSignBlockAsync(int idWell, int idDailyReport, SignBlockDto signBlock, CancellationToken cancellationToken) =>
UpdateBlockAsync(idWell, idDailyReport, signBlock, cancellationToken); UpdateBlockAsync(idWell, idDailyReport, signBlock, cancellationToken);
/// <summary> /// <summary>
@ -109,10 +115,10 @@ public class DailyReportController : ControllerBase
public Task<IActionResult> UpdateSubsystemBlockAsync(int idWell, int idDailyReport, SubsystemBlockDto subsystemBlock, public Task<IActionResult> UpdateSubsystemBlockAsync(int idWell, int idDailyReport, SubsystemBlockDto subsystemBlock,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var validSubsystemIds = new[] { 100000, 100001 }; var validSubsystemNames = new[] { "АвтоСПО", "Автопроработка" };
if (subsystemBlock.Modules.Any(m => !validSubsystemIds.Contains(m.IdSubsystem))) if (subsystemBlock.Modules.Any(m => !validSubsystemNames.Contains(m.SubsystemName)))
throw new ArgumentInvalidException($"Возможно добавить модули только с Id: {string.Join(", ", validSubsystemIds)}", throw new ArgumentInvalidException($"Возможно добавить модули только с именами {string.Join(", ", validSubsystemNames)}",
nameof(subsystemBlock.Modules)); nameof(subsystemBlock.Modules));
return UpdateBlockAsync(idWell, idDailyReport, subsystemBlock, cancellationToken); return UpdateBlockAsync(idWell, idDailyReport, subsystemBlock, cancellationToken);
@ -133,7 +139,7 @@ public class DailyReportController : ControllerBase
public Task<IActionResult> UpdateTimeBalanceBlockAsync(int idWell, int idDailyReport, TimeBalanceBlockDto timeBalanceBlock, public Task<IActionResult> UpdateTimeBalanceBlockAsync(int idWell, int idDailyReport, TimeBalanceBlockDto timeBalanceBlock,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var validWellOperationsIds = new[] { 4001, 4002, 4004, 4012 }; var validWellOperationsIds = new[] { 1, 2, 3, 4 };
if (timeBalanceBlock.WellOperations.Any(o => !validWellOperationsIds.Contains(o.IdWellOperation))) if (timeBalanceBlock.WellOperations.Any(o => !validWellOperationsIds.Contains(o.IdWellOperation)))
throw new ArgumentInvalidException($"Возможно добавить операции только с Id: {string.Join(", ", validWellOperationsIds)}", throw new ArgumentInvalidException($"Возможно добавить операции только с Id: {string.Join(", ", validWellOperationsIds)}",
@ -187,18 +193,25 @@ public class DailyReportController : ControllerBase
/// Экспорт суточного рапорта /// Экспорт суточного рапорта
/// </summary> /// </summary>
/// <param name="idWell">Id скважины</param> /// <param name="idWell">Id скважины</param>
/// <param name="dailyReportDateStart">Дата формирования суточного отчёта</param> /// <param name="dateStart">Дата формирования суточного отчёта</param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet("{dailyReportDateStart}")] [HttpGet("{dateStart}")]
[ProducesResponseType(typeof(PhysicalFileResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(PhysicalFileResult), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> ExportAsync(int idWell, DateOnly dailyReportDateStart, CancellationToken cancellationToken) public async Task<IActionResult> ExportAsync(int idWell, DateOnly dateStart, CancellationToken cancellationToken)
{ {
var dateStartToDateTime = dateStart.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc);
var datesRange = await dailyReportService.GetDatesRangeAsync(idWell, cancellationToken);
if (dateStartToDateTime < datesRange?.From || dateStartToDateTime > datesRange?.To)
throw new ArgumentInvalidException("Невозможно получить суточный отчёт", nameof(dateStart));
await AssertUserAccessToWell(idWell, cancellationToken); await AssertUserAccessToWell(idWell, cancellationToken);
var dailyReport = await dailyReportExportService.ExportAsync(idWell, var dailyReport = await dailyReportExportService.ExportAsync(idWell,
dailyReportDateStart.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc), cancellationToken); dateStart.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc), cancellationToken);
return File(dailyReport.File, "application/octet-stream", dailyReport.FileName); return File(dailyReport.File, "application/octet-stream", dailyReport.FileName);
} }