153 lines
5.2 KiB
C#
153 lines
5.2 KiB
C#
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using LiteCqrs;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using TeleWave.Application.Broadcast;
|
|
using TeleWave.Application.Common.Interfaces;
|
|
using TeleWave.Application.Common.Models;
|
|
using TeleWave.Application.Programming.Templates;
|
|
using TeleWave.Domain.Programming;
|
|
using TeleWave.Domain.Programming.Planning;
|
|
|
|
namespace TeleWave.Application.Programming.Planning.Trace;
|
|
|
|
public sealed class GetEntryTraceQueryHandler(IAppDbContext dbContext)
|
|
: IQueryHandler<GetEntryTraceQuery, Result<EntryTraceDto>>
|
|
{
|
|
/// <summary>Те же настройки, что при записи трейса генератором.</summary>
|
|
private static readonly JsonSerializerOptions TraceJsonOptions = new()
|
|
{
|
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
Converters = { new JsonStringEnumConverter() },
|
|
};
|
|
|
|
public async Task<Result<EntryTraceDto>> Handle(
|
|
GetEntryTraceQuery query,
|
|
CancellationToken cancellationToken
|
|
)
|
|
{
|
|
var entry = await dbContext
|
|
.ScheduleEntries.AsNoTracking()
|
|
.FirstOrDefaultAsync(e => e.Id == query.EntryId, cancellationToken);
|
|
if (entry is null)
|
|
return Result.Failure<EntryTraceDto>(ChannelErrors.NotFound);
|
|
|
|
var showName = entry.ShowId is { } showId
|
|
? await dbContext
|
|
.Shows.AsNoTracking()
|
|
.Where(s => s.Id == showId)
|
|
.Select(s => s.Name)
|
|
.FirstOrDefaultAsync(cancellationToken)
|
|
: null;
|
|
|
|
var collectionName = entry.CollectionId is { } collectionId
|
|
? await dbContext
|
|
.Collections.AsNoTracking()
|
|
.Where(c => c.Id == collectionId)
|
|
.Select(c => c.Name)
|
|
.FirstOrDefaultAsync(cancellationToken)
|
|
: null;
|
|
|
|
var trace = Parse(entry.TraceJson);
|
|
if (trace is null)
|
|
return Result.Success(
|
|
new EntryTraceDto(
|
|
entry.Id,
|
|
entry.StartsAtUtc,
|
|
entry.EndsAtUtc,
|
|
showName,
|
|
entry.EpisodeIndex,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
collectionName,
|
|
null,
|
|
null,
|
|
null,
|
|
0,
|
|
false,
|
|
null
|
|
)
|
|
);
|
|
|
|
// Слот мог быть удалён или изменён после генерации — трейс от этого не портится, просто
|
|
// часть подписей окажется пустой.
|
|
var slot = trace.SlotId is { } slotId
|
|
? await dbContext
|
|
.Slots.AsNoTracking()
|
|
.FirstOrDefaultAsync(s => s.Id == slotId, cancellationToken)
|
|
: null;
|
|
var layer = slot is null
|
|
? null
|
|
: await dbContext
|
|
.GridLayers.AsNoTracking()
|
|
.FirstOrDefaultAsync(l => l.Id == slot.LayerId, cancellationToken);
|
|
|
|
var group = slot?.GroupId is { } groupId
|
|
? await dbContext
|
|
.Groups.AsNoTracking()
|
|
.Where(g => g.Id == groupId)
|
|
.Select(g => new { g.Name, g.ItemCount })
|
|
.FirstOrDefaultAsync(cancellationToken)
|
|
: null;
|
|
|
|
var strategy = SlotStrategy.FromJson(slot?.StrategyJson);
|
|
|
|
var junctionId = slot?.JunctionAfterId ?? slot?.JunctionBetweenId;
|
|
var junctionName = junctionId is { } id
|
|
? await dbContext
|
|
.JunctionTemplates.AsNoTracking()
|
|
.Where(j => j.Id == id)
|
|
.Select(j => j.Name)
|
|
.FirstOrDefaultAsync(cancellationToken)
|
|
: null;
|
|
|
|
return Result.Success(
|
|
new EntryTraceDto(
|
|
entry.Id,
|
|
entry.StartsAtUtc,
|
|
entry.EndsAtUtc,
|
|
showName,
|
|
entry.EpisodeIndex,
|
|
layer?.Name,
|
|
layer?.Priority,
|
|
slot?.Title,
|
|
trace.SlotKind,
|
|
slot?.Weekday,
|
|
slot?.TargetStart,
|
|
slot?.TargetDurationMinutes,
|
|
group?.Name,
|
|
group?.ItemCount,
|
|
collectionName,
|
|
trace.Strategy,
|
|
strategy?.CooldownDays,
|
|
trace.CandidatesAfterCooldown,
|
|
trace.DriftMinutes,
|
|
trace.Snapped,
|
|
junctionName
|
|
)
|
|
);
|
|
}
|
|
|
|
private static PlanTrace? Parse(string? json)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(json))
|
|
return null;
|
|
|
|
try
|
|
{
|
|
return JsonSerializer.Deserialize<PlanTrace>(json, TraceJsonOptions);
|
|
}
|
|
catch (JsonException)
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
}
|