111 lines
4.0 KiB
C#
111 lines
4.0 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using TeleWave.Application.Common.Interfaces;
|
|
using TeleWave.Application.Common.Models;
|
|
using TeleWave.Domain.Programming;
|
|
|
|
namespace TeleWave.Application.Programming.Templates;
|
|
|
|
/// <summary>
|
|
/// Общая часть создания и правки слота: проверки, которые нельзя доверить валидатору, потому что
|
|
/// им нужны соседние слоты и справочник групп.
|
|
/// </summary>
|
|
public sealed class SlotWriter(IAppDbContext dbContext)
|
|
{
|
|
/// <summary>
|
|
/// Проверяет вход и применяет его к слоту. <paramref name="slot"/> = null — проверка перед
|
|
/// созданием.
|
|
/// </summary>
|
|
public async Task<Result> ApplyAsync(
|
|
GridLayer layer,
|
|
Slot? slot,
|
|
SlotInput input,
|
|
CancellationToken cancellationToken
|
|
)
|
|
{
|
|
// Перекрытие внутри слоя запрещено: два слота на одну минуту сделали бы выбор неоднозначным.
|
|
if (
|
|
layer.HasOverlap(
|
|
input.Weekday,
|
|
input.TargetStart,
|
|
input.TargetDurationMinutes,
|
|
slot?.Id
|
|
)
|
|
)
|
|
return Result.Failure(TemplateErrors.SlotsOverlap);
|
|
|
|
if (input.SlotKind == SlotKind.Content)
|
|
{
|
|
if (input.GroupId is not { } groupId)
|
|
return Result.Failure(TemplateErrors.GroupRequired);
|
|
|
|
if (!await dbContext.Groups.AnyAsync(g => g.Id == groupId, cancellationToken))
|
|
return Result.Failure(TemplateErrors.GroupNotFound);
|
|
}
|
|
|
|
if (input.SlotKind == SlotKind.Repeat && input.RepeatSource is null)
|
|
return Result.Failure(TemplateErrors.RepeatSourceRequired);
|
|
|
|
var target =
|
|
slot
|
|
?? layer.AddSlot(
|
|
input.Title,
|
|
input.TargetStart,
|
|
input.TargetDurationMinutes,
|
|
input.Daypart,
|
|
input.SlotKind,
|
|
input.Weekday
|
|
);
|
|
|
|
target.UpdateTiming(
|
|
input.Weekday,
|
|
input.TargetStart,
|
|
input.TargetDurationMinutes,
|
|
input.Daypart,
|
|
input.IsAnchor,
|
|
input.MaxDriftMinutes,
|
|
input.SnapToMinutes
|
|
);
|
|
target.UpdateContent(
|
|
new SlotContent(
|
|
input.Title,
|
|
input.SlotKind,
|
|
input.GroupId,
|
|
input.Strategy?.ToJson(),
|
|
input.RepeatSource?.ToJson(),
|
|
input.BlockMode,
|
|
input.BlockValue,
|
|
input.OverflowPolicy,
|
|
input.JunctionBetweenId,
|
|
input.JunctionAfterId
|
|
)
|
|
);
|
|
|
|
return Result.Success();
|
|
}
|
|
|
|
/// <summary>Загружает шаблон целиком по слою — правка слота меняет ревизию всего шаблона.</summary>
|
|
public Task<ScheduleTemplate?> LoadTemplateByLayerAsync(
|
|
Guid layerId,
|
|
CancellationToken cancellationToken
|
|
) =>
|
|
dbContext
|
|
.ScheduleTemplates.Include(t => t.Layers)
|
|
.ThenInclude(l => l.Slots)
|
|
.AsSplitQuery()
|
|
.FirstOrDefaultAsync(t => t.Layers.Any(l => l.Id == layerId), cancellationToken);
|
|
|
|
/// <summary>Загружает шаблон по слоту.</summary>
|
|
public Task<ScheduleTemplate?> LoadTemplateBySlotAsync(
|
|
Guid slotId,
|
|
CancellationToken cancellationToken
|
|
) =>
|
|
dbContext
|
|
.ScheduleTemplates.Include(t => t.Layers)
|
|
.ThenInclude(l => l.Slots)
|
|
.AsSplitQuery()
|
|
.FirstOrDefaultAsync(
|
|
t => t.Layers.Any(l => l.Slots.Any(s => s.Id == slotId)),
|
|
cancellationToken
|
|
);
|
|
}
|