Архивация списков #13
@@ -3,6 +3,7 @@ using FluentResults;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Tnsu.ApprovalRoute.Application.ListPartitioning;
|
||||
using Tnsu.ApprovalRoute.Domain.Entities;
|
||||
using Tnsu.ApprovalRoute.Infrastructure.Persistence;
|
||||
using Tnsu.ApprovalRoute.Infrastructure.SharePoint;
|
||||
@@ -20,6 +21,7 @@ namespace Tnsu.ApprovalRoute.Application.Reminders;
|
||||
public sealed class ReminderService(
|
||||
ApprovalRouteDbContext db,
|
||||
ISharePointClient sharePoint,
|
||||
ListPartitionRegistry partitionRegistry,
|
||||
IOptions<RemindersOptions> options,
|
||||
ILogger<ReminderService> logger)
|
||||
{
|
||||
@@ -57,14 +59,63 @@ public sealed class ReminderService(
|
||||
return Result.Ok<IReadOnlyList<ReminderDto>>(reminders);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обходит ВСЕ партиции типа заявки (архив + текущая) — заявка, уехавшая в
|
||||
/// архив, продолжает ждать чьего-то решения точно так же, как в текущем
|
||||
/// списке, и не должна пропадать из напоминаний только из-за того, что для
|
||||
/// новых заявок завели следующий список.
|
||||
///
|
||||
/// EnsureCurrentRegisteredAsync — на случай, если этот тип заявок ни разу не
|
||||
/// читали через ListPartitionService (сам реестр партиций мог ещё не знать
|
||||
/// про текущий список): без неё серия была бы пустой и напоминания молча не
|
||||
/// строились бы даже по текущему списку.
|
||||
/// </summary>
|
||||
private async Task<IReadOnlyList<ReminderDto>> GetOverdueForTypeAsync(
|
||||
RequestType type, DateTimeOffset now, CancellationToken cancellationToken)
|
||||
{
|
||||
await partitionRegistry.EnsureCurrentRegisteredAsync(type, cancellationToken);
|
||||
|
||||
var partitions = await db.RequestListPartitions.AsNoTracking()
|
||||
.Where(x => x.RequestTypeId == type.Id)
|
||||
.OrderBy(x => x.Number)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var reminders = new List<ReminderDto>();
|
||||
foreach (var partition in partitions)
|
||||
{
|
||||
try
|
||||
{
|
||||
reminders.AddRange(await GetOverdueForPartitionAsync(type, partition, now, cancellationToken));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Недоступная партиция (например, архивный список переименовали
|
||||
// или удалили вручную) не должна обнулять напоминания по
|
||||
// остальным партициям того же типа.
|
||||
logger.LogError(
|
||||
e, "Напоминания по списку {ListId} (партиция №{Number}) типа «{Type}» не собраны",
|
||||
partition.ListId, partition.Number, type.Name);
|
||||
}
|
||||
}
|
||||
|
||||
return reminders;
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyList<ReminderDto>> GetOverdueForPartitionAsync(
|
||||
RequestType type, RequestListPartition partition, DateTimeOffset now, CancellationToken cancellationToken)
|
||||
{
|
||||
var siteUrl = type.SiteUrl!;
|
||||
var dataListTitle = await sharePoint.GetListTitleAsync(siteUrl, type.ListId!, cancellationToken);
|
||||
|
||||
// Читаем Title ЖИВЬЁМ, а не из partition.Title (закешированного на момент
|
||||
// создания партиции) — тот же принцип, что был здесь и раньше для
|
||||
// единственного списка: заголовок могли переименовать вручную, и кеш
|
||||
// тогда соврёт, а по url список найдётся всегда.
|
||||
var dataListTitle = await sharePoint.GetListTitleAsync(siteUrl, partition.ListId, cancellationToken);
|
||||
if (string.IsNullOrWhiteSpace(dataListTitle))
|
||||
{
|
||||
logger.LogWarning("Список данных типа «{Type}» не найден по url {Url}", type.Name, type.ListId);
|
||||
logger.LogWarning(
|
||||
"Список данных партиции №{Number} типа «{Type}» не найден по url {Url}",
|
||||
partition.Number, type.Name, partition.ListId);
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using Shouldly;
|
||||
using Tnsu.ApprovalRoute.Application.ListPartitioning;
|
||||
using Tnsu.ApprovalRoute.Application.Reminders;
|
||||
using Tnsu.ApprovalRoute.Application.RequestTypes;
|
||||
using Tnsu.ApprovalRoute.Infrastructure.Persistence;
|
||||
@@ -95,6 +96,7 @@ public sealed class ReminderServiceTests : IClassFixture<PostgresDatabaseFixture
|
||||
return new ReminderService(
|
||||
_services.GetRequiredService<ApprovalRouteDbContext>(),
|
||||
sharePoint.Object,
|
||||
_services.GetRequiredService<ListPartitionRegistry>(),
|
||||
Options.Create(new RemindersOptions()),
|
||||
NullLogger<ReminderService>.Instance);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user