using LiteCqrs; using TeleWave.Api.Common; using TeleWave.Application.Library; using TeleWave.Application.Library.AddEpisode; using TeleWave.Application.Library.BulkTagShows; using TeleWave.Application.Library.CreateShow; using TeleWave.Application.Library.DeleteShow; using TeleWave.Application.Library.GetShow; using TeleWave.Application.Library.ListShows; using TeleWave.Application.Library.RemoveEpisode; using TeleWave.Application.Library.RenameShow; using TeleWave.Application.Library.SetShowAudience; using TeleWave.Application.Library.SetShowGenres; using TeleWave.Application.Library.SetShowOriginalName; using TeleWave.Application.Metadata.EnrichShows; using TeleWave.Domain.Library; using TeleWave.Infrastructure.Identity; namespace TeleWave.Api.Endpoints; public static class ShowEndpoints { public static IEndpointRouteBuilder MapShowEndpoints(this IEndpointRouteBuilder app) { var admin = app.MapGroup("/api/admin/shows") .WithTags("Admin.Shows") .RequireAuthorization(policy => policy.RequireRole(RoleNames.Admin)); admin.MapPost("", CreateShow).Produces(StatusCodes.Status201Created); admin.MapGet("", ListShows).Produces>(); admin.MapGet("/{id:guid}", GetShow).Produces(); admin.MapPut("/{id:guid}/name", Rename).Produces(StatusCodes.Status204NoContent); admin .MapPut("/{id:guid}/original-name", SetOriginalName) .Produces(StatusCodes.Status204NoContent); admin.MapPut("/{id:guid}/audience", SetAudience).Produces(StatusCodes.Status204NoContent); admin.MapPut("/{id:guid}/genres", SetGenres).Produces(StatusCodes.Status204NoContent); admin.MapDelete("/{id:guid}", DeleteShow).Produces(StatusCodes.Status204NoContent); // Массовые операции над отмеченными шоу: разметка руками и обогащение из источника. admin.MapPost("/bulk/tag", BulkTag).Produces(); admin.MapPost("/bulk/enrich", BulkEnrich).Produces(); admin .MapPost("/{id:guid}/episodes", AddEpisode) .Produces(StatusCodes.Status201Created); admin .MapDelete("/{id:guid}/episodes/{episodeId:guid}", RemoveEpisode) .Produces(StatusCodes.Status204NoContent); return app; } private static async Task CreateShow( CreateShowCommand command, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send(command, cancellationToken); return result.IsSuccess ? Results.Created( $"/api/admin/shows/{result.Value}", new CreatedIdResponse(result.Value) ) : result.ToHttpResult(); } private static async Task ListShows( ISender sender, CancellationToken cancellationToken, Guid? genreId = null, bool interstitials = false ) { var result = await sender.Send( new ListShowsQuery(genreId, interstitials), cancellationToken ); return Results.Ok(result); } private static async Task GetShow( Guid id, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send(new GetShowQuery(id), cancellationToken); return result.ToHttpResult(); } private static async Task Rename( Guid id, RenameShowBody body, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send(new RenameShowCommand(id, body.Name), cancellationToken); return result.ToHttpResult(); } private static async Task SetOriginalName( Guid id, SetShowOriginalNameBody body, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send( new SetShowOriginalNameCommand(id, body.OriginalName), cancellationToken ); return result.ToHttpResult(); } private static async Task SetAudience( Guid id, SetShowAudienceBody body, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send( new SetShowAudienceCommand(id, body.Audience), cancellationToken ); return result.ToHttpResult(); } private static async Task SetGenres( Guid id, SetShowGenresBody body, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send( new SetShowGenresCommand(id, body.GenreIds, body.PrimaryGenreId), cancellationToken ); return result.ToHttpResult(); } private static async Task DeleteShow( Guid id, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send(new DeleteShowCommand(id), cancellationToken); return result.ToHttpResult(); } private static async Task AddEpisode( Guid id, AddEpisodeBody body, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send( new AddEpisodeCommand(id, body.MediaAssetId), cancellationToken ); return result.IsSuccess ? Results.Created($"/api/admin/shows/{id}", new CreatedIdResponse(result.Value)) : result.ToHttpResult(); } private static async Task RemoveEpisode( Guid id, Guid episodeId, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send(new RemoveEpisodeCommand(id, episodeId), cancellationToken); return result.ToHttpResult(); } private static async Task BulkTag( BulkTagShowsBody body, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send( new BulkTagShowsCommand( body.ShowIds, body.GenreIds, body.ReplaceGenres, body.Audience, body.SetAudience ), cancellationToken ); return result.IsSuccess ? Results.Ok(new BulkTaggedResponse(result.Value)) : result.ToHttpResult(); } private static async Task BulkEnrich( BulkEnrichBody body, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send( new EnrichShowsMetadataCommand(body.ShowIds, body.Provider), cancellationToken ); return result.ToHttpResult(); } } public sealed record AddEpisodeBody(Guid MediaAssetId); /// Массовая разметка: жанры и рейтинг применяются независимо друг от друга. public sealed record BulkTagShowsBody( IReadOnlyList ShowIds, IReadOnlyList? GenreIds = null, bool ReplaceGenres = false, ShowAudience? Audience = null, bool SetAudience = false ); public sealed record BulkEnrichBody(IReadOnlyList ShowIds, string Provider); /// Скольких шоу коснулась массовая правка. public sealed record BulkTaggedResponse(int Updated); public sealed record RenameShowBody(string Name); public sealed record SetShowOriginalNameBody(string? OriginalName); /// Рейтинг шоу; null — снять проставленный. public sealed record SetShowAudienceBody(ShowAudience? Audience); public sealed record SetShowGenresBody(IReadOnlyList GenreIds, Guid? PrimaryGenreId);