diff --git a/AsbCloudApp/Data/DailyReport/HeadDto.cs b/AsbCloudApp/Data/DailyReport/HeadDto.cs index e32dd63c..173d4e63 100644 --- a/AsbCloudApp/Data/DailyReport/HeadDto.cs +++ b/AsbCloudApp/Data/DailyReport/HeadDto.cs @@ -29,7 +29,7 @@ namespace AsbCloudApp.Data.DailyReport /// /// дата рапорта /// - public DateTime ReportDate { get; set; } + public DateOnly ReportDate { get; set; } /// /// глубина забоя на дату начала интервала diff --git a/AsbCloudApp/Services/IDailyReportService.cs b/AsbCloudApp/Services/IDailyReportService.cs index 865985c0..67cfcc4e 100644 --- a/AsbCloudApp/Services/IDailyReportService.cs +++ b/AsbCloudApp/Services/IDailyReportService.cs @@ -20,7 +20,7 @@ namespace AsbCloudApp.Services /// /// /// - Task> GetListAsync(int idWell, DateTime? begin, DateTime? end, CancellationToken cancellationToken); + Task> GetListAsync(int idWell, DateOnly? begin, DateOnly? end, CancellationToken cancellationToken); /// /// Добавить новый рапорт @@ -30,7 +30,7 @@ namespace AsbCloudApp.Services /// /// /// - Task AddAsync(int idWell, DateTime startDate, int idUser, CancellationToken token); + Task AddAsync(int idWell, DateOnly startDate, int idUser, CancellationToken token); /// /// Сформировать файл рапорта @@ -39,7 +39,7 @@ namespace AsbCloudApp.Services /// /// /// - Task MakeReportAsync(int idWell, DateTime date, CancellationToken token); + Task MakeReportAsync(int idWell, DateOnly date, CancellationToken token); /// /// изменить блок данных для суточного рапорта @@ -49,6 +49,6 @@ namespace AsbCloudApp.Services /// /// /// - Task UpdateBlockAsync(int idWell, DateTime startDate, ItemInfoDto dto, CancellationToken token); + Task UpdateBlockAsync(int idWell, DateOnly startDate, ItemInfoDto dto, CancellationToken token); } } diff --git a/AsbCloudDb/EFExtentions.cs b/AsbCloudDb/EFExtentions.cs index a00f2899..575480e3 100644 --- a/AsbCloudDb/EFExtentions.cs +++ b/AsbCloudDb/EFExtentions.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.Json.Serialization; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -258,4 +260,17 @@ namespace AsbCloudDb } } + public class DateOnlyJsonConverter : JsonConverter + { + public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return DateOnly.FromDateTime(reader.GetDateTime()); + } + + public override void Write(Utf8JsonWriter writer, DateOnly value, JsonSerializerOptions options) + { + var isoDate = value.ToString("O"); + writer.WriteStringValue(isoDate); + } + } } diff --git a/AsbCloudDb/Model/DailyReport/Head.cs b/AsbCloudDb/Model/DailyReport/Head.cs index 42f6f857..fc5542e8 100644 --- a/AsbCloudDb/Model/DailyReport/Head.cs +++ b/AsbCloudDb/Model/DailyReport/Head.cs @@ -1,4 +1,6 @@ using System; +using System.Text.Json.Serialization; + namespace AsbCloudDb.Model.DailyReport { public class Head : ItemInfo @@ -26,7 +28,8 @@ namespace AsbCloudDb.Model.DailyReport /// /// дата рапорта /// - public DateTime ReportDate { get; set; } + [JsonConverter(typeof(DateOnlyJsonConverter))] + public DateOnly ReportDate { get; set; } /// /// глубина забоя на дату начала интервала diff --git a/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs b/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs index 32853c0c..11be6762 100644 --- a/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs +++ b/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs @@ -39,7 +39,7 @@ namespace AsbCloudInfrastructure.Services.DailyReport } - public async Task> GetListAsync(int idWell, DateTime? begin, DateTime? end, CancellationToken token) + public async Task> GetListAsync(int idWell, DateOnly? begin, DateOnly? end, CancellationToken token) { var well = wellService.GetOrDefault(idWell); if (well is null || well.Timezone is null) @@ -49,14 +49,12 @@ namespace AsbCloudInfrastructure.Services.DailyReport if (begin is not null) { - var beginDateOnly = ExtractDate(begin.Value, well); - query = query.Where(d => d.StartDate >= beginDateOnly); + query = query.Where(d => d.StartDate >= begin); } if (end is not null) { - var endDateOnly = ExtractDate(end.Value, well); - query = query.Where(d => d.StartDate <= endDateOnly); + query = query.Where(d => d.StartDate <= end); } var entities = await query.OrderByDescending(e => e.StartDate) @@ -89,23 +87,22 @@ namespace AsbCloudInfrastructure.Services.DailyReport return factOperations; } - public async Task AddAsync(int idWell, DateTime startDate, int idUser, CancellationToken token) + public async Task AddAsync(int idWell, DateOnly startDate, int idUser, CancellationToken token) { var well = wellService.GetOrDefault(idWell); if (well is null) throw new ArgumentInvalidException("idWell doesn`t exist", nameof(idWell)); - var startDateOnly = ExtractDate(startDate, well); var hasEntity = await db.DailyReports - .AnyAsync(r => r.IdWell == idWell && r.StartDate == startDateOnly, token); + .AnyAsync(r => r.IdWell == idWell && r.StartDate == startDate, token); if (hasEntity) - throw new ArgumentInvalidException($"daily report on {startDateOnly} already exists", nameof(startDateOnly)); + throw new ArgumentInvalidException($"daily report on {startDate} already exists", nameof(startDate)); var entity = new AsbCloudDb.Model.DailyReport.DailyReport { IdWell = idWell, - StartDate = startDateOnly, + StartDate = startDate, Info = new DailyReportInfo() { Head = CreateHeadDailyReportBlock(well, startDate, idUser) @@ -116,14 +113,13 @@ namespace AsbCloudInfrastructure.Services.DailyReport return result; } - public async Task UpdateBlockAsync(int idWell, DateTime startDate, ItemInfoDto dto, CancellationToken token) + public async Task UpdateBlockAsync(int idWell, DateOnly startDate, ItemInfoDto dto, CancellationToken token) { var well = wellService.GetOrDefault(idWell); if (well is null) throw new ArgumentInvalidException("idWell doesn`t exist", nameof(idWell)); - var startDateOnly = DateOnly.FromDateTime(startDate); - var entity = await db.DailyReports.FirstOrDefaultAsync(r => r.IdWell == idWell && r.StartDate == startDateOnly, token); + var entity = await db.DailyReports.FirstOrDefaultAsync(r => r.IdWell == idWell && r.StartDate == startDate, token); if (entity is null) throw new ArgumentInvalidException("Daily report doesn`t exist", nameof(startDate)); @@ -145,7 +141,7 @@ namespace AsbCloudInfrastructure.Services.DailyReport return result; } - public async Task MakeReportAsync(int idWell, DateTime date, CancellationToken token) + public async Task MakeReportAsync(int idWell, DateOnly date, CancellationToken token) { var stageIds = WellOperationCategory.WorkStages.Select(w => w.Id).ToArray(); var wellOperationCategories = wellOperationRepository.GetCategories(true) @@ -161,14 +157,10 @@ namespace AsbCloudInfrastructure.Services.DailyReport return memoryStream; } - private async Task GetOrDefaultAsync(int idWell, DateTime date, CancellationToken token) + private async Task GetOrDefaultAsync(int idWell, DateOnly date, CancellationToken token) { - var dateOffset = date.Date; var entity = await db.DailyReports - .FirstOrDefaultAsync(r => r.IdWell == idWell && - r.StartDate.Year == dateOffset.Year && - r.StartDate.DayOfYear == dateOffset.DayOfYear - , token); + .FirstOrDefaultAsync(r => r.IdWell == idWell && r.StartDate == date, token); if (entity is null) throw new ArgumentInvalidException("Daily report doesn`t exist", nameof(date)); @@ -222,19 +214,6 @@ namespace AsbCloudInfrastructure.Services.DailyReport return dto; } - /// - /// Приведение данных к формату DateOnly с учетом часового пояса скважины - /// - /// - /// - /// - private DateOnly ExtractDate(DateTime dateTime, WellDto well) - { - var dateTimeOffset = dateTime.ToUtcDateTimeOffset(well!.Timezone.Hours); - var date = new DateOnly(dateTimeOffset.Year, dateTimeOffset.Month, dateTimeOffset.Day); - return date; - } - /// /// Создание блока "Заголовок" по умолчанию /// @@ -242,14 +221,14 @@ namespace AsbCloudInfrastructure.Services.DailyReport /// /// /// - private Head CreateHeadDailyReportBlock(WellDto well, DateTime startDate, int idUser) + private Head CreateHeadDailyReportBlock(WellDto well, DateOnly startDate, int idUser) { var customer = well.Companies.FirstOrDefault(company => company.IdCompanyType == 1); var contractor = well.Companies.FirstOrDefault(company => company.IdCompanyType == 2); return new Head() { - ReportDate = startDate.Date, + ReportDate = startDate, WellName = well.Caption, ClusterName = well?.Cluster ?? string.Empty, Customer = customer?.Caption ?? string.Empty, diff --git a/AsbCloudWebApi/Controllers/DailyReportController.cs b/AsbCloudWebApi/Controllers/DailyReportController.cs index 9f357e59..11183a4e 100644 --- a/AsbCloudWebApi/Controllers/DailyReportController.cs +++ b/AsbCloudWebApi/Controllers/DailyReportController.cs @@ -45,7 +45,7 @@ namespace AsbCloudWebApi.Controllers /// [HttpGet] [ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)] - public async Task GetListAsync(int idWell, DateTime? begin, DateTime? end, CancellationToken token) + public async Task GetListAsync(int idWell, DateOnly? begin, DateOnly? end, CancellationToken token) { var result = await dailyReportService.GetListAsync(idWell, begin, end, token); return Ok(result); @@ -60,7 +60,7 @@ namespace AsbCloudWebApi.Controllers /// [HttpPost] [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] - public async Task AddAsync(int idWell, [Required] DateTime startDate, CancellationToken token) + public async Task AddAsync(int idWell, [Required] DateOnly startDate, CancellationToken token) { if (!await UserHasAccesToWellAsync(idWell, token)) return Forbid(); @@ -81,7 +81,7 @@ namespace AsbCloudWebApi.Controllers /// [HttpPut("{date}/head")] [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] - public Task UpdateHeadAsync(int idWell, [Required] DateTime date, [Required] HeadDto dto, CancellationToken token) + public Task UpdateHeadAsync(int idWell, [Required] DateOnly date, [Required] HeadDto dto, CancellationToken token) => UpdateReportBlockAsync(idWell, date, dto, token); /// @@ -94,7 +94,7 @@ namespace AsbCloudWebApi.Controllers /// [HttpPut("{date}/bha")] [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] - public Task UpdateBhaAsync(int idWell, [Required] DateTime date, [Required] BhaDto dto, CancellationToken token) + public Task UpdateBhaAsync(int idWell, [Required] DateOnly date, [Required] BhaDto dto, CancellationToken token) => UpdateReportBlockAsync(idWell, date, dto, token); /// @@ -107,7 +107,7 @@ namespace AsbCloudWebApi.Controllers /// [HttpPut("{date}/noDrilling")] [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] - public Task UpdateNoDrillingAsync(int idWell, [Required] DateTime date, [Required] NoDrillingDto dto, CancellationToken token) + public Task UpdateNoDrillingAsync(int idWell, [Required] DateOnly date, [Required] NoDrillingDto dto, CancellationToken token) => UpdateReportBlockAsync(idWell, date, dto, token); /// @@ -120,7 +120,7 @@ namespace AsbCloudWebApi.Controllers /// [HttpPut("{date}/saub")] [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] - public Task UpdateSaubAsync(int idWell, [Required] DateTime date, [Required] SaubDto dto, CancellationToken token) + public Task UpdateSaubAsync(int idWell, [Required] DateOnly date, [Required] SaubDto dto, CancellationToken token) => UpdateReportBlockAsync(idWell, date, dto, token); /// @@ -133,7 +133,7 @@ namespace AsbCloudWebApi.Controllers /// [HttpPut("{date}/sign")] [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] - public Task UpdateSignAsync(int idWell, [Required] DateTime date, [Required] SignDto dto, CancellationToken token) + public Task UpdateSignAsync(int idWell, [Required] DateOnly date, [Required] SignDto dto, CancellationToken token) => UpdateReportBlockAsync(idWell, date, dto, token); /// @@ -144,7 +144,7 @@ namespace AsbCloudWebApi.Controllers /// /// /// - private async Task UpdateReportBlockAsync(int idWell, DateTime date, ItemInfoDto dto, CancellationToken token) + private async Task UpdateReportBlockAsync(int idWell, DateOnly date, ItemInfoDto dto, CancellationToken token) { if (!await UserHasAccesToWellAsync(idWell, token)) return Forbid(); @@ -165,7 +165,7 @@ namespace AsbCloudWebApi.Controllers /// [HttpGet("{date}/excel")] [ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK)] - public async Task DownloadAsync(int idWell, DateTime date, CancellationToken token) + public async Task DownloadAsync(int idWell, DateOnly date, CancellationToken token) { if (!await UserHasAccesToWellAsync(idWell, token)) return Forbid(); diff --git a/AsbCloudWebApi/Converters/DateOnlyTypeConverter.cs b/AsbCloudWebApi/Converters/DateOnlyTypeConverter.cs new file mode 100644 index 00000000..df826075 --- /dev/null +++ b/AsbCloudWebApi/Converters/DateOnlyTypeConverter.cs @@ -0,0 +1,48 @@ +using System; +using System.ComponentModel; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace AsbCloudWebApi.Converters +{ +#nullable enable + public class DateOnlyTypeConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) + { + if (sourceType == typeof(string)) + { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) + { + if (value is string str) + { + return DateOnly.Parse(str); + } + return base.ConvertFrom(context, culture, value); + } + + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) + { + if (destinationType == typeof(string)) + { + return true; + } + return base.CanConvertTo(context, destinationType); + } + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) + { + if (destinationType == typeof(string) && value is DateOnly date) + { + return date.ToString("O"); + } + return base.ConvertTo(context, culture, value, destinationType); + } + } +#nullable disable +} diff --git a/AsbCloudWebApi/Converters/TimeOnlyTypeConverter.cs b/AsbCloudWebApi/Converters/TimeOnlyTypeConverter.cs index 484d2f3e..ceb40b04 100644 --- a/AsbCloudWebApi/Converters/TimeOnlyTypeConverter.cs +++ b/AsbCloudWebApi/Converters/TimeOnlyTypeConverter.cs @@ -5,7 +5,7 @@ using System.Globalization; namespace AsbCloudWebApi.Converters { #nullable enable - public class DateOnlyTypeConverter : TypeConverter + public class TimeOnlyTypeConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { diff --git a/AsbCloudWebApi/DependencyInjection.cs b/AsbCloudWebApi/DependencyInjection.cs index f25a5494..25b66545 100644 --- a/AsbCloudWebApi/DependencyInjection.cs +++ b/AsbCloudWebApi/DependencyInjection.cs @@ -3,11 +3,9 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Reflection; using System.Threading.Tasks; @@ -19,6 +17,7 @@ namespace AsbCloudWebApi { services.AddSwaggerGen(c => { + c.MapType(() => new OpenApiSchema { Type = "string", Format = "date" }); c.CustomOperationIds(e => { return $"{e.ActionDescriptor.RouteValues["action"]}"; diff --git a/AsbCloudWebApi/Extentions.cs b/AsbCloudWebApi/Extentions.cs index 1179c25e..aff16969 100644 --- a/AsbCloudWebApi/Extentions.cs +++ b/AsbCloudWebApi/Extentions.cs @@ -1,9 +1,10 @@ using AsbCloudApp.Data; -using System.Collections.Generic; -using System.Linq; +using AsbCloudWebApi.Converters; +using System; +using System.ComponentModel; using System.Security.Claims; -namespace Microsoft.AspNetCore.Mvc +namespace Microsoft.AspNetCore.Mvc { public static class Extentions { @@ -38,48 +39,12 @@ namespace Microsoft.AspNetCore.Mvc }); } - public static BadRequestBuilder BadRequestBuilder(this ControllerBase controller, string paramName, params string[] errors) - => new BadRequestBuilder(paramName, errors); - } - - public class BadRequestBuilder - { - private readonly Dictionary> body; - - private List GetOrCreateNew(string paramName) + public static MvcOptions UseDateOnlyTimeOnlyStringConverters(this MvcOptions options) { - List par; - if (body.ContainsKey(paramName)) - par = body[paramName]; - else - { - par = new List(); - body[paramName] = par; - } - return par; + TypeDescriptor.AddAttributes(typeof(DateOnly), new TypeConverterAttribute(typeof(DateOnlyTypeConverter))); + return options; } - public BadRequestBuilder(string paramName, params string[] errors) - { - body = new(); - body[paramName] = new List(errors); - } - - public BadRequestBuilder Add(string paramName, params string[] errors) - { - var par = GetOrCreateNew(paramName); - par.AddRange(errors); - return this; - } - - public BadRequestObjectResult Build() - { - var o = body.Select(e => new { name = e.Key, errors = e.Value.ToArray() }); - - return new BadRequestObjectResult(o); - } - - public static implicit operator BadRequestObjectResult(BadRequestBuilder d) => d.Build(); } } diff --git a/AsbCloudWebApi/Startup.cs b/AsbCloudWebApi/Startup.cs index 96d408a5..14d8db0f 100644 --- a/AsbCloudWebApi/Startup.cs +++ b/AsbCloudWebApi/Startup.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.AspNetCore.Mvc; namespace AsbCloudWebApi { @@ -31,6 +32,8 @@ namespace AsbCloudWebApi })) .AddProtoBufNet(); + services.AddControllers(options => options.UseDateOnlyTimeOnlyStringConverters()); + ProtobufModel.EnshureRegistered(); services.AddSwagger(); @@ -90,6 +93,7 @@ namespace AsbCloudWebApi }); }); + } public void Configure(IApplicationBuilder app, IWebHostEnvironment env)