Update scheduling parameters and refactor channel endpoints: extend HorizonDays to 7 and RetentionDays to 90 in appsettings.json. Consolidate channel-related endpoint logic by removing obsolete files and enhancing the ShowEndpoints with audience and genre management capabilities. Improve error handling and streamline command handlers for channel operations.
build / backend (push) Successful in 7m40s
build / frontend (push) Failing after 39s
tests / backend-tests (push) Successful in 6m9s

This commit is contained in:
Leonid Pershin
2026-07-26 13:32:13 +03:00
parent c4ef954dea
commit 66040a8841
272 changed files with 27944 additions and 8699 deletions
@@ -0,0 +1,72 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace TeleWave.Application.Programming.Templates;
/// <summary>Как слот выбирает элемент группы.</summary>
public enum SlotStrategyType
{
/// <summary>Элементы группы по порядку позиций.</summary>
Sequential = 0,
/// <summary>Случайный элемент, не показанный последние N дней.</summary>
RandomWithCooldown = 1,
/// <summary>Всегда один и тот же элемент.</summary>
Fixed = 2,
}
/// <summary>Что делать, когда остывание отсекло всех кандидатов.</summary>
public enum CooldownFallback
{
/// <summary>Взять элемент, не игравший дольше всех.</summary>
OldestFirst = 0,
/// <summary>Проигнорировать остывание на этот выход.</summary>
IgnoreCooldown = 1,
}
/// <summary>
/// Стратегия выбора элемента внутри слота. Отвечает только за выбор элемента: единицы внутри элемента
/// всегда идут по порядку от курсора — сериал не должен прыгать по сериям.
///
/// Размер блока живёт на слоте отдельно и ортогонален стратегии: любую можно скомбинировать
/// с «одна единица», «четыре подряд» или «пока не наберётся два часа».
/// </summary>
public sealed record SlotStrategy(
SlotStrategyType Type = SlotStrategyType.Sequential,
/// <summary>Начинать группу заново, дойдя до конца (иначе слот останавливается).</summary>
bool RestartOnEnd = true,
/// <summary>Дней остывания для <see cref="SlotStrategyType.RandomWithCooldown"/>.</summary>
int CooldownDays = 0,
CooldownFallback Fallback = CooldownFallback.OldestFirst,
/// <summary>Закреплённый элемент для <see cref="SlotStrategyType.Fixed"/>.</summary>
Guid? FixedElementId = null
)
{
private static readonly JsonSerializerOptions Options = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Converters = { new JsonStringEnumConverter() },
};
public string ToJson() => JsonSerializer.Serialize(this, Options);
/// <summary>Разбирает сохранённую стратегию. Повреждённый JSON — как отсутствие стратегии:
/// слот получит поведение по умолчанию, а не уронит генерацию всего канала.</summary>
public static SlotStrategy? FromJson(string? json)
{
if (string.IsNullOrWhiteSpace(json))
return null;
try
{
return JsonSerializer.Deserialize<SlotStrategy>(json, Options);
}
catch (JsonException)
{
return null;
}
}
}