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.
This commit is contained in:
@@ -1,217 +1,205 @@
|
||||
using LiteCqrs;
|
||||
using TeleWave.Api.Common;
|
||||
using TeleWave.Application.Broadcast;
|
||||
using TeleWave.Application.Broadcast.CreateChannel;
|
||||
using TeleWave.Application.Broadcast.GetChannel;
|
||||
using TeleWave.Application.Broadcast.GetSchedule;
|
||||
using TeleWave.Application.Broadcast.ListChannels;
|
||||
using TeleWave.Application.Broadcast.RegenerateSchedule;
|
||||
using TeleWave.Application.Broadcast.UpdateChannelSettings;
|
||||
using TeleWave.Domain.Broadcast;
|
||||
using TeleWave.Infrastructure.Identity;
|
||||
|
||||
namespace TeleWave.Api.Endpoints;
|
||||
|
||||
/// <summary>
|
||||
/// Админ-эндпоинты канала. Реализация разнесена по partial-файлам под-ресурсов:
|
||||
/// <c>ChannelEndpoints.Shows.cs</c> (шоу+реклама), <c>ChannelEndpoints.Bumpers.cs</c>
|
||||
/// (блоки/подблоки/файлы/preview), <c>ChannelEndpoints.Overrides.cs</c> (override'ы). Здесь —
|
||||
/// регистрация всех маршрутов и хендлеры уровня канала (создание/список/настройки/расписание).
|
||||
/// </summary>
|
||||
public static partial class ChannelEndpoints
|
||||
{
|
||||
public static IEndpointRouteBuilder MapChannelEndpoints(this IEndpointRouteBuilder app)
|
||||
{
|
||||
var admin = app.MapGroup("/api/admin/channels")
|
||||
.WithTags("Admin.Channels")
|
||||
.RequireAuthorization(policy => policy.RequireRole(RoleNames.Admin));
|
||||
|
||||
admin.MapPost("", CreateChannel).Produces<CreatedIdResponse>(StatusCodes.Status201Created);
|
||||
admin.MapGet("", ListChannels).Produces<IReadOnlyList<ChannelSummaryDto>>();
|
||||
admin.MapGet("/{id:guid}", GetChannel).Produces<ChannelDto>();
|
||||
admin
|
||||
.MapPut("/{id:guid}/settings", UpdateSettings)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
|
||||
admin
|
||||
.MapPost("/{id:guid}/shows", AddShow)
|
||||
.Produces<CreatedIdResponse>(StatusCodes.Status201Created);
|
||||
admin
|
||||
.MapPut("/{id:guid}/shows/{channelShowId:guid}", UpdateShow)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
admin
|
||||
.MapDelete("/{id:guid}/shows/{channelShowId:guid}", RemoveShow)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
|
||||
admin
|
||||
.MapPost("/{id:guid}/ads", AddAd)
|
||||
.Produces<CreatedIdResponse>(StatusCodes.Status201Created);
|
||||
admin
|
||||
.MapDelete("/{id:guid}/ads/{channelAdId:guid}", RemoveAd)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
|
||||
admin
|
||||
.MapPost("/{id:guid}/bumper/templates", AddBumperTemplate)
|
||||
.Produces<CreatedIdResponse>(StatusCodes.Status201Created);
|
||||
admin
|
||||
.MapPut("/{id:guid}/bumper/templates/{templateId:guid}", UpdateBumperTemplate)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
admin
|
||||
.MapDelete("/{id:guid}/bumper/templates/{templateId:guid}", RemoveBumperTemplate)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
admin
|
||||
.MapPut("/{id:guid}/bumper/templates/{templateId:guid}/audio", UploadTemplateAudio)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
admin
|
||||
.MapDelete("/{id:guid}/bumper/templates/{templateId:guid}/audio", ClearTemplateAudio)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
admin
|
||||
.MapPut(
|
||||
"/{id:guid}/bumper/templates/{templateId:guid}/background",
|
||||
SetTemplateBackground
|
||||
)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
admin
|
||||
.MapDelete(
|
||||
"/{id:guid}/bumper/templates/{templateId:guid}/background",
|
||||
ClearTemplateBackground
|
||||
)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
|
||||
admin
|
||||
.MapPost("/{id:guid}/bumper/templates/{templateId:guid}/preview", RenderPreview)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
admin.MapGet(
|
||||
"/{id:guid}/bumper/templates/{templateId:guid}/preview/{variantId:guid}/index.m3u8",
|
||||
PreviewPlaylist
|
||||
);
|
||||
admin.MapGet(
|
||||
"/{id:guid}/bumper/templates/{templateId:guid}/preview/{variantId:guid}/{file}",
|
||||
PreviewSegment
|
||||
);
|
||||
|
||||
admin
|
||||
.MapPost("/{id:guid}/bumper/templates/{templateId:guid}/variants", AddBumperVariant)
|
||||
.Produces<CreatedIdResponse>(StatusCodes.Status201Created);
|
||||
admin
|
||||
.MapPut(
|
||||
"/{id:guid}/bumper/templates/{templateId:guid}/variants/{variantId:guid}",
|
||||
UpdateBumperVariant
|
||||
)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
admin
|
||||
.MapDelete(
|
||||
"/{id:guid}/bumper/templates/{templateId:guid}/variants/{variantId:guid}",
|
||||
RemoveBumperVariant
|
||||
)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
|
||||
admin
|
||||
.MapPost("/{id:guid}/overrides", CreateOverride)
|
||||
.Produces<CreatedIdResponse>(StatusCodes.Status201Created);
|
||||
admin
|
||||
.MapDelete("/{id:guid}/overrides/{overrideId:guid}", DeleteOverride)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
|
||||
admin.MapPost("/{id:guid}/regenerate", Regenerate).Produces(StatusCodes.Status204NoContent);
|
||||
admin
|
||||
.MapGet("/{id:guid}/schedule", GetSchedule)
|
||||
.Produces<IReadOnlyList<ScheduleEntryDto>>();
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
private static async Task<IResult> CreateChannel(
|
||||
CreateChannelCommand command,
|
||||
ISender sender,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var result = await sender.Send(command, cancellationToken);
|
||||
return result.IsSuccess
|
||||
? Results.Created(
|
||||
$"/api/admin/channels/{result.Value}",
|
||||
new CreatedIdResponse(result.Value)
|
||||
)
|
||||
: result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> ListChannels(
|
||||
ISender sender,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var result = await sender.Send(new ListChannelsQuery(), cancellationToken);
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetChannel(
|
||||
Guid id,
|
||||
ISender sender,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var result = await sender.Send(new GetChannelQuery(id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> UpdateSettings(
|
||||
Guid id,
|
||||
UpdateChannelSettingsBody body,
|
||||
ISender sender,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var result = await sender.Send(
|
||||
new UpdateChannelSettingsCommand(
|
||||
id,
|
||||
body.Name,
|
||||
body.IsEnabled,
|
||||
body.AdInsertion,
|
||||
body.AdsPerBreak,
|
||||
body.BumpersEnabled,
|
||||
body.Bumper,
|
||||
body.FillerAssetId
|
||||
),
|
||||
cancellationToken
|
||||
);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> Regenerate(
|
||||
Guid id,
|
||||
ISender sender,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var result = await sender.Send(new RegenerateChannelScheduleCommand(id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetSchedule(
|
||||
Guid id,
|
||||
DateTimeOffset? from,
|
||||
DateTimeOffset? to,
|
||||
ISender sender,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var fromUtc = from ?? DateTimeOffset.UtcNow;
|
||||
var toUtc = to ?? fromUtc.AddDays(1);
|
||||
var result = await sender.Send(
|
||||
new GetChannelScheduleQuery(id, fromUtc, toUtc),
|
||||
cancellationToken
|
||||
);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record UpdateChannelSettingsBody(
|
||||
string Name,
|
||||
bool IsEnabled,
|
||||
AdInsertion AdInsertion,
|
||||
int AdsPerBreak,
|
||||
bool BumpersEnabled,
|
||||
BumperSettingsInput Bumper,
|
||||
Guid? FillerAssetId
|
||||
);
|
||||
using LiteCqrs;
|
||||
using TeleWave.Api.Common;
|
||||
using TeleWave.Application.Broadcast;
|
||||
using TeleWave.Application.Broadcast.CreateChannel;
|
||||
using TeleWave.Application.Broadcast.GetChannel;
|
||||
using TeleWave.Application.Broadcast.GetSchedule;
|
||||
using TeleWave.Application.Broadcast.ListChannels;
|
||||
using TeleWave.Application.Broadcast.UpdateChannelSettings;
|
||||
using TeleWave.Application.Broadcast.UpdateChannelTime;
|
||||
using TeleWave.Domain.Broadcast;
|
||||
using TeleWave.Infrastructure.Identity;
|
||||
|
||||
namespace TeleWave.Api.Endpoints;
|
||||
|
||||
/// <summary>
|
||||
/// Админ-эндпоинты канала: создание, список, настройки, время и чтение расписания. Заставки —
|
||||
/// в <c>ChannelEndpoints.Bumpers.cs</c>. Что и когда идёт в эфире, задаёт шаблон сетки
|
||||
/// (<c>TemplateEndpoints</c>).
|
||||
/// </summary>
|
||||
public static partial class ChannelEndpoints
|
||||
{
|
||||
public static IEndpointRouteBuilder MapChannelEndpoints(this IEndpointRouteBuilder app)
|
||||
{
|
||||
var admin = app.MapGroup("/api/admin/channels")
|
||||
.WithTags("Admin.Channels")
|
||||
.RequireAuthorization(policy => policy.RequireRole(RoleNames.Admin));
|
||||
|
||||
admin.MapPost("", CreateChannel).Produces<CreatedIdResponse>(StatusCodes.Status201Created);
|
||||
admin.MapGet("", ListChannels).Produces<IReadOnlyList<ChannelSummaryDto>>();
|
||||
admin.MapGet("/{id:guid}", GetChannel).Produces<ChannelDto>();
|
||||
admin
|
||||
.MapPut("/{id:guid}/settings", UpdateSettings)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
admin.MapPut("/{id:guid}/time", UpdateTime).Produces(StatusCodes.Status204NoContent);
|
||||
|
||||
admin
|
||||
.MapPost("/{id:guid}/bumper/templates", AddBumperTemplate)
|
||||
.Produces<CreatedIdResponse>(StatusCodes.Status201Created);
|
||||
admin
|
||||
.MapPut("/{id:guid}/bumper/templates/{templateId:guid}", UpdateBumperTemplate)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
admin
|
||||
.MapDelete("/{id:guid}/bumper/templates/{templateId:guid}", RemoveBumperTemplate)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
admin
|
||||
.MapPut("/{id:guid}/bumper/templates/{templateId:guid}/audio", UploadTemplateAudio)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
admin
|
||||
.MapDelete("/{id:guid}/bumper/templates/{templateId:guid}/audio", ClearTemplateAudio)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
admin
|
||||
.MapPut(
|
||||
"/{id:guid}/bumper/templates/{templateId:guid}/background",
|
||||
SetTemplateBackground
|
||||
)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
admin
|
||||
.MapDelete(
|
||||
"/{id:guid}/bumper/templates/{templateId:guid}/background",
|
||||
ClearTemplateBackground
|
||||
)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
|
||||
admin
|
||||
.MapPost("/{id:guid}/bumper/templates/{templateId:guid}/preview", RenderPreview)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
admin.MapGet(
|
||||
"/{id:guid}/bumper/templates/{templateId:guid}/preview/{variantId:guid}/index.m3u8",
|
||||
PreviewPlaylist
|
||||
);
|
||||
admin.MapGet(
|
||||
"/{id:guid}/bumper/templates/{templateId:guid}/preview/{variantId:guid}/{file}",
|
||||
PreviewSegment
|
||||
);
|
||||
|
||||
admin
|
||||
.MapPost("/{id:guid}/bumper/templates/{templateId:guid}/variants", AddBumperVariant)
|
||||
.Produces<CreatedIdResponse>(StatusCodes.Status201Created);
|
||||
admin
|
||||
.MapPut(
|
||||
"/{id:guid}/bumper/templates/{templateId:guid}/variants/{variantId:guid}",
|
||||
UpdateBumperVariant
|
||||
)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
admin
|
||||
.MapDelete(
|
||||
"/{id:guid}/bumper/templates/{templateId:guid}/variants/{variantId:guid}",
|
||||
RemoveBumperVariant
|
||||
)
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
|
||||
admin
|
||||
.MapGet("/{id:guid}/schedule", GetSchedule)
|
||||
.Produces<IReadOnlyList<ScheduleEntryDto>>();
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
private static async Task<IResult> CreateChannel(
|
||||
CreateChannelCommand command,
|
||||
ISender sender,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var result = await sender.Send(command, cancellationToken);
|
||||
return result.IsSuccess
|
||||
? Results.Created(
|
||||
$"/api/admin/channels/{result.Value}",
|
||||
new CreatedIdResponse(result.Value)
|
||||
)
|
||||
: result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> ListChannels(
|
||||
ISender sender,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var result = await sender.Send(new ListChannelsQuery(), cancellationToken);
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetChannel(
|
||||
Guid id,
|
||||
ISender sender,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var result = await sender.Send(new GetChannelQuery(id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> UpdateTime(
|
||||
Guid id,
|
||||
UpdateChannelTimeBody body,
|
||||
ISender sender,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var result = await sender.Send(
|
||||
new UpdateChannelTimeCommand(
|
||||
id,
|
||||
body.Number,
|
||||
body.UtcOffsetMinutes,
|
||||
body.DayStartTime
|
||||
),
|
||||
cancellationToken
|
||||
);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> UpdateSettings(
|
||||
Guid id,
|
||||
UpdateChannelSettingsBody body,
|
||||
ISender sender,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var result = await sender.Send(
|
||||
new UpdateChannelSettingsCommand(
|
||||
id,
|
||||
body.Name,
|
||||
body.IsEnabled,
|
||||
body.BumpersEnabled,
|
||||
body.Bumper,
|
||||
body.FillerAssetId
|
||||
),
|
||||
cancellationToken
|
||||
);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
|
||||
private static async Task<IResult> GetSchedule(
|
||||
Guid id,
|
||||
DateTimeOffset? from,
|
||||
DateTimeOffset? to,
|
||||
ISender sender,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var fromUtc = from ?? DateTimeOffset.UtcNow;
|
||||
var toUtc = to ?? fromUtc.AddDays(1);
|
||||
var result = await sender.Send(
|
||||
new GetChannelScheduleQuery(id, fromUtc, toUtc),
|
||||
cancellationToken
|
||||
);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Номер канала и его время: смещение от UTC и начало вещательных суток.</summary>
|
||||
public sealed record UpdateChannelTimeBody(
|
||||
int? Number,
|
||||
int UtcOffsetMinutes,
|
||||
TimeOnly DayStartTime
|
||||
);
|
||||
|
||||
public sealed record UpdateChannelSettingsBody(
|
||||
string Name,
|
||||
bool IsEnabled,
|
||||
bool BumpersEnabled,
|
||||
BumperSettingsInput Bumper,
|
||||
Guid? FillerAssetId
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user