feat(api, dal): в SettingsFromDb добавлен параметр WeekendCacheTtl - время кэша для выходных. Доработан сервис WeekendDayService с учетом кэша. В апи добавлен контроллер по управлению выходными днями WeekendController

This commit is contained in:
Mikhail Trubnikov
2024-09-17 11:55:55 +10:00
parent 251e3a20b0
commit 2a1c4ac4a8
16 changed files with 3348 additions and 24 deletions

View File

@@ -426,6 +426,18 @@
#endregion #endregion
public static class Weekend
{
public const string GetAll = Base + "/weekends/";
public const string Get = Base + "/weekends/" + getParamDate;
public const string Create = Base + "/weekends/";
public const string Delete = Base + "/weekends/" + getParamDate;
public const string getParam = "{id}";
public const string getParamDate = "{date}";
}
} }
} }

View File

@@ -0,0 +1,7 @@
namespace PARR.API.Contracts.V1.Requests
{
public class WeekendRequest
{
public DateOnly WeekendDate { get; set; }
}
}

View File

@@ -0,0 +1,8 @@
namespace PARR.API.Contracts.V1.Responses
{
public class WeekendResponse
{
//public Guid Id { get; set; }
public DateOnly Date { get; set; }
}
}

View File

@@ -0,0 +1,128 @@
using AutoMapper;
using FluentValidation;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using PARR.API.Contracts.V1;
using PARR.API.Contracts.V1.Requests;
using PARR.API.Contracts.V1.Requests.Queries;
using PARR.API.Contracts.V1.Responses;
using PARR.API.Contracts.V1.Responses.Base;
using PARR.API.Controllers.V1.Base;
using PARR.API.Extensions;
using PARR.API.Services.Interfaces;
using PARR.Constants;
using PARR.DAL.DomainModels;
using PARR.DAL.Models;
using PARR.DAL.Services.Interfaces;
namespace PARR.API.Controllers.V1
{
/// <summary>
/// Выходные дни
/// </summary>
[Authorize(Roles = ParrRoles.Administrator.Role)]
public class WeekendController : BaseApiController
{
private readonly IMapper mapper;
private readonly IWeekendDayService weekendDayService;
private readonly IUriService uriService;
private readonly IValidator<WeekendRequest> validator;
public WeekendController(
IMapper mapper,
IWeekendDayService weekendDayService,
IUriService uriService,
IValidator<WeekendRequest> validator
)
{
this.mapper = mapper;
this.weekendDayService = weekendDayService;
this.uriService = uriService;
this.validator = validator;
}
/// <summary>
/// Получить список выходных дней
/// </summary>
/// <returns></returns>
[HttpGet(ApiRoutes.Weekend.GetAll)]
public async Task<IActionResult> GetAll([FromQuery] PaginationQuery paginationQuery)
{
var paginationFilter = mapper.Map<PaginationFilter>(paginationQuery);
IQueryable<WeekendDay> query = weekendDayService.Get().OrderBy(t => t.Date);
var weekends = await weekendDayService.GetPage(query, paginationFilter).ToListAsync();
if (!weekends.Any())
return NoContent();
var response = mapper.Map<List<WeekendResponse>>(weekends);
var paginationResponse = new PagedResponse<WeekendResponse>(response, true).GetPaginatedProps(paginationFilter, query);
return Ok(paginationResponse);
}
/// <summary>
/// Получить выходной
/// </summary>
/// <returns></returns>
[HttpGet(ApiRoutes.Weekend.Get)]
public async Task<IActionResult> Get([FromRoute] DateOnly date)
{
var weekend = await weekendDayService.Get().FirstOrDefaultAsync(t => t.Date == date);
if (weekend == null)
return NotFound();
var response = mapper.Map<WeekendResponse>(weekend);
return Ok(new Response<WeekendResponse>(response, true));
}
/// <summary>
/// Добавить выходной
/// </summary>
/// <returns></returns>
[HttpPost(ApiRoutes.Weekend.Create)]
public async Task<IActionResult> Create([FromBody] WeekendRequest request)
{
var resultValidate = await validator.ValidateAsync(request);
if (!resultValidate.IsValid)
return BadRequest(new Response(resultValidate.Errors));
var weekend = new WeekendDay { Id = Guid.NewGuid(), Date = request.WeekendDate };
if (!await weekendDayService.CreateAsync(weekend) || !await weekendDayService.CommitAsync())
return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = "Ошибка при добавлении выходного дня." } }));
var locationUri = uriService.GetUri(ApiRoutes.Weekend.Get, ApiRoutes.Weekend.getParamDate, weekend.Date.ToString());
return Created(locationUri, new Response<WeekendResponse>(mapper.Map<WeekendResponse>(weekend), true));
}
/// <summary>
/// Удалить выходной
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
[HttpDelete(ApiRoutes.Weekend.Delete)]
public async Task<IActionResult> Delete([FromRoute] DateOnly date)
{
var weekend = await weekendDayService.Get().FirstOrDefaultAsync(t => t.Date == date);
if (weekend == null)
return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Ошибка при удалении выходного дня. Не найден выходной." } }));
if (!weekendDayService.Delete(weekend) || !await weekendDayService.CommitAsync())
return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Ошибка при удалении выходного дня." } }));
return NoContent();
}
}
}

View File

@@ -297,6 +297,9 @@ namespace PARR.API.MappingProfiles
CreateMap<EkStatus, EkStatusResponse>(); CreateMap<EkStatus, EkStatusResponse>();
CreateMap<WeekendDay, WeekendResponse>();
} }
} }
} }

View File

@@ -0,0 +1,29 @@
using FluentValidation;
using Microsoft.EntityFrameworkCore;
using PARR.API.Contracts.V1.Requests;
using PARR.DAL.Services.Interfaces;
namespace PARR.API.Validators
{
public class WeekendRequestValidator : AbstractValidator<WeekendRequest>
{
private readonly IWeekendDayService weekendDayService;
public WeekendRequestValidator(IWeekendDayService weekendDayService)
{
this.weekendDayService = weekendDayService;
RuleFor(t => t.WeekendDate).MustAsync(async (entity, value, c) => await IsEmptyAsync(entity.WeekendDate)
).WithMessage("Данная запись уже существует.");
}
private async Task<bool> IsEmptyAsync(DateOnly date)
{
var exist = await weekendDayService.Get().FirstOrDefaultAsync(t => t.Date == date);
if (exist == null)
return true;
return false;
}
}
}

View File

@@ -109,7 +109,8 @@ namespace PARR.DAL.Context
new { Name = nameof(SettingsFromDb.ScheduleExclude), Description = "Расписание регламентной работы - Тип исключения", Value = "Нет исключений" }, new { Name = nameof(SettingsFromDb.ScheduleExclude), Description = "Расписание регламентной работы - Тип исключения", Value = "Нет исключений" },
new { Name = nameof(SettingsFromDb.ScheduleRepeatRange), Description = "Расписание регламентной работы - Диапазн повторов", Value = "Отсутствует дата завершения" }, new { Name = nameof(SettingsFromDb.ScheduleRepeatRange), Description = "Расписание регламентной работы - Диапазн повторов", Value = "Отсутствует дата завершения" },
new { Name = nameof(SettingsFromDb.OrderSearchDeltaDate), Description = "Промежуток времени для поиска нарядов в ЕСПП", Value = new TimeSpan(1, 30, 0).ToString() }, new { Name = nameof(SettingsFromDb.OrderSearchDeltaDate), Description = "Промежуток времени для поиска нарядов в ЕСПП", Value = new TimeSpan(1, 30, 0).ToString() },
new { Name = nameof(SettingsFromDb.EsppRobotAccountTimeZoneHour), Description = "Таймзона УЗ роботов в ЕСПП, в часах (может быть положительная и отрицательная)", Value = "3" } new { Name = nameof(SettingsFromDb.EsppRobotAccountTimeZoneHour), Description = "Таймзона УЗ роботов в ЕСПП, в часах (может быть положительная и отрицательная)", Value = "3" },
new { Name = nameof(SettingsFromDb.WeekendCacheTtl), Description = "Время хранения в кэше данных о выходных и рабочих днях", Value = new TimeSpan(1, 0, 0).ToString() }
); );
}); });
#endregion #endregion

View File

@@ -46,7 +46,7 @@
/// <summary> /// <summary>
/// Расписание регламентной работы - В каком часовом поясе /// Расписание регламентной работы - В каком часовом поясе
/// </summary> /// </summary>
public string ScheduleTimezone { get; set; } = string.Empty; public string ScheduleTimezone { get; set; } = string.Empty;
/// <summary> /// <summary>
/// Расписание регламентной работы - Тип исключения /// Расписание регламентной работы - Тип исключения
@@ -68,6 +68,11 @@
/// </summary> /// </summary>
public int EsppRobotAccountTimeZoneHour { get; set; } public int EsppRobotAccountTimeZoneHour { get; set; }
/// <summary>
/// Время хранения в кэше данных о выходных и рабочих днях
/// </summary>
public TimeSpan WeekendCacheTtl { get; set; } = new TimeSpan(0, 5, 0);
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace PARR.DAL.Migrations
{
/// <inheritdoc />
public partial class tblSettingsAddWeekendCacheTtl : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.InsertData(
table: "Settings",
columns: new[] { "Name", "Description", "Value" },
values: new object[] { "WeekendCacheTtl", "Время хранения в кэше данных о выходных и рабочих днях", "01:00:00" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DeleteData(
table: "Settings",
keyColumn: "Name",
keyValue: "WeekendCacheTtl");
}
}
}

View File

@@ -2208,6 +2208,12 @@ namespace PARR.DAL.Migrations
Name = "EsppRobotAccountTimeZoneHour", Name = "EsppRobotAccountTimeZoneHour",
Description = "Таймзона УЗ роботов в ЕСПП, в часах (может быть положительная и отрицательная)", Description = "Таймзона УЗ роботов в ЕСПП, в часах (может быть положительная и отрицательная)",
Value = "3" Value = "3"
},
new
{
Name = "WeekendCacheTtl",
Description = "Время хранения в кэше данных о выходных и рабочих днях",
Value = "01:00:00"
}); });
}); });

View File

@@ -119,10 +119,6 @@ namespace PARR.DAL
services.AddSingleton(settingsFromDb); services.AddSingleton(settingsFromDb);
PrefixSettings.PrefixWithoutVariable = settingsFromDb.TemplatePrefixWithoutVariable; PrefixSettings.PrefixWithoutVariable = settingsFromDb.TemplatePrefixWithoutVariable;
var weekendCacheSettings = new WeekendCacheSettings();
configuration.GetSection(nameof(WeekendCacheSettings)).Bind(weekendCacheSettings);
services.AddSingleton(weekendCacheSettings);
} }
} }

View File

@@ -2,10 +2,10 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using PARR.DAL.CacheServices; using PARR.DAL.CacheServices;
using PARR.DAL.Context; using PARR.DAL.Context;
using PARR.DAL.Contracts;
using PARR.DAL.Models; using PARR.DAL.Models;
using PARR.DAL.Services.Abstracts; using PARR.DAL.Services.Abstracts;
using PARR.DAL.Services.Interfaces; using PARR.DAL.Services.Interfaces;
using PARR.DAL.Settings;
namespace PARR.DAL.Services.Implementations namespace PARR.DAL.Services.Implementations
{ {
@@ -14,7 +14,7 @@ namespace PARR.DAL.Services.Implementations
private readonly DataContext dataContext; private readonly DataContext dataContext;
private readonly ILogger<WeekendDayService> logger; private readonly ILogger<WeekendDayService> logger;
private readonly IRedisCacheService redisCacheService; private readonly IRedisCacheService redisCacheService;
private readonly WeekendCacheSettings weekendCacheSettings; private readonly SettingsFromDb settings;
protected override DbSet<WeekendDay> EntitySet => dataContext.WeekendDays; protected override DbSet<WeekendDay> EntitySet => dataContext.WeekendDays;
@@ -24,13 +24,13 @@ namespace PARR.DAL.Services.Implementations
DataContext dataContext, DataContext dataContext,
ILogger<WeekendDayService> logger, ILogger<WeekendDayService> logger,
IRedisCacheService redisCacheService, IRedisCacheService redisCacheService,
WeekendCacheSettings weekendCacheSettings SettingsFromDb settings
) : base(logger) ) : base(logger)
{ {
this.dataContext = dataContext; this.dataContext = dataContext;
this.logger = logger; this.logger = logger;
this.redisCacheService = redisCacheService; this.redisCacheService = redisCacheService;
this.weekendCacheSettings = weekendCacheSettings; this.settings = settings;
} }
@@ -40,7 +40,6 @@ namespace PARR.DAL.Services.Implementations
} }
public async Task<bool> IsWorkDayAsync(DateOnly date, bool useCache = false) public async Task<bool> IsWorkDayAsync(DateOnly date, bool useCache = false)
{ {
if (useCache) if (useCache)
@@ -62,13 +61,45 @@ namespace PARR.DAL.Services.Implementations
//сохраняем всегда в КЭШ значение выходного и рабочего дня //сохраняем всегда в КЭШ значение выходного и рабочего дня
var cacheKey = isWorkday ? GetWorkDayKey(date) : GetWeekendKey(date); var cacheKey = isWorkday ? GetWorkDayKey(date) : GetWeekendKey(date);
//TODO:!!! Тут настройки вынести в DB settings await redisCacheService.SetCachedDataAsync(cacheKey, date, settings.WeekendCacheTtl);
await redisCacheService.SetCachedDataAsync(cacheKey, date, weekendCacheSettings.WeekendTtl);
return isWorkday; return isWorkday;
} }
public override async Task<bool> CreateAsync(WeekendDay obj)
{
// При создании выходного дня, смотрим, был ли он в кэше, если был, удаляем
await redisCacheService.DeleteCachedDataAsync(GetWeekendKey(obj.Date));
await redisCacheService.DeleteCachedDataAsync(GetWorkDayKey(obj.Date));
return await base.CreateAsync(obj);
}
public override bool Delete(WeekendDay obj)
{
// При создании выходного дня, смотрим, был ли он в кэше, если был, удаляем
redisCacheService.DeleteCachedData(GetWeekendKey(obj.Date));
redisCacheService.DeleteCachedData(GetWorkDayKey(obj.Date));
return base.Delete(obj);
}
public override async Task<bool> DeleteAsync(Guid id)
{
var exist = await GetAsync(id);
if (exist == null)
{
logger.LogError($"Ошибка при удалении из БД. Не найдена запись в БД с id: {id}");
return false;
}
return Delete(exist);
}
private string GetWeekendKey(DateOnly day) private string GetWeekendKey(DateOnly day)
{ {
return $"weekend_{day.ToString("yyyy-MM-dd")}"; return $"weekend_{day.ToString("yyyy-MM-dd")}";

View File

@@ -6,6 +6,13 @@ namespace PARR.DAL.Services.Interfaces
public interface IWeekendDayService : IBaseService<WeekendDay> public interface IWeekendDayService : IBaseService<WeekendDay>
{ {
IQueryable<DateOnly> GetWeekends(DateOnly start, DateOnly end); IQueryable<DateOnly> GetWeekends(DateOnly start, DateOnly end);
/// <summary>
/// Проверка, это рабочий день?
/// </summary>
/// <param name="date"></param>
/// <param name="useCache">разрешить использовать кэш? По умолчанию false</param>
/// <returns></returns>
Task<bool> IsWorkDayAsync(DateOnly date, bool useCache = false); Task<bool> IsWorkDayAsync(DateOnly date, bool useCache = false);
} }
} }

View File

@@ -1,10 +0,0 @@
namespace PARR.DAL.Settings
{
public class WeekendCacheSettings
{
/// <summary>
/// Время хранения в кэше данных о выходных и рабочих днях
/// </summary>
public TimeSpan WeekendTtl { get; set; } = new TimeSpan(0, 5, 0);
}
}

View File

@@ -2,7 +2,7 @@ version: '3.4'
#PARR JOB AUTO CONTROL #PARR JOB AUTO CONTROL
services: services:
parr-template-activator: parr-job-auto-control:
image: harbor.dvgd.rzd/parr/parr-job-auto-control:${tag-latest} image: harbor.dvgd.rzd/parr/parr-job-auto-control:${tag-latest}
environment: environment:
- ASPNETCORE_ENVIRONMENT=Production - ASPNETCORE_ENVIRONMENT=Production