Implement bulk tagging and enriching of shows with new API endpoints and frontend integration
Added new API endpoints for bulk tagging and enriching shows, allowing for mass updates of genres and audience ratings. Implemented backend logic to handle bulk operations and updated the Dependency Injection configuration to include necessary services. Enhanced the frontend with new components for selecting shows and applying bulk actions, improving the user experience for managing multiple shows simultaneously. Localization updates were made to support these new features in both English and Russian.
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
using LiteCqrs;
|
||||
using TeleWave.Application.Common.Models;
|
||||
using TeleWave.Domain.Library;
|
||||
|
||||
namespace TeleWave.Application.Library.BulkTagShows;
|
||||
|
||||
/// <summary>
|
||||
/// Проставляет жанры и/или возрастной рейтинг сразу нескольким шоу. Оба поля необязательны и
|
||||
/// применяются независимо: обычно правят что-то одно, а связывать их в одну форму значило бы
|
||||
/// заставлять переставлять второе «как было».
|
||||
/// </summary>
|
||||
/// <param name="GenreIds">
|
||||
/// Что делать с жанрами: null — не трогать. Иначе <paramref name="ReplaceGenres"/> решает, заменить
|
||||
/// набор целиком или дописать к имеющимся.
|
||||
/// </param>
|
||||
/// <param name="ReplaceGenres">
|
||||
/// true — заменить набор жанров переданным; false — дописать. Дописывание нужно чаще: у шоу уже
|
||||
/// есть жанры из метаданных, и разметка «это ещё и детское» не должна их стирать.
|
||||
/// </param>
|
||||
/// <param name="Audience">Рейтинг: null со <paramref name="SetAudience"/> = true снимает проставленный.</param>
|
||||
/// <param name="SetAudience">Трогать ли рейтинг вообще.</param>
|
||||
public sealed record BulkTagShowsCommand(
|
||||
IReadOnlyList<Guid> ShowIds,
|
||||
IReadOnlyList<Guid>? GenreIds = null,
|
||||
bool ReplaceGenres = false,
|
||||
ShowAudience? Audience = null,
|
||||
bool SetAudience = false
|
||||
) : ICommand<Result<int>>;
|
||||
@@ -0,0 +1,66 @@
|
||||
using LiteCqrs;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TeleWave.Application.Common.Interfaces;
|
||||
using TeleWave.Application.Common.Models;
|
||||
using TeleWave.Application.Library.Genres;
|
||||
|
||||
namespace TeleWave.Application.Library.BulkTagShows;
|
||||
|
||||
public sealed class BulkTagShowsCommandHandler(IAppDbContext dbContext)
|
||||
: ICommandHandler<BulkTagShowsCommand, Result<int>>
|
||||
{
|
||||
public async Task<Result<int>> Handle(
|
||||
BulkTagShowsCommand command,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var showIds = command.ShowIds.Distinct().ToList();
|
||||
if (showIds.Count == 0)
|
||||
return Result.Success(0);
|
||||
|
||||
var genreIds = command.GenreIds?.Distinct().ToList();
|
||||
if (genreIds is { Count: > 0 })
|
||||
{
|
||||
// Ссылка на несуществующий жанр упала бы нарушением внешнего ключа — проверяем заранее.
|
||||
var known = await dbContext
|
||||
.Genres.Where(g => genreIds.Contains(g.Id))
|
||||
.CountAsync(cancellationToken);
|
||||
if (known != genreIds.Count)
|
||||
return Result.Failure<int>(GenreErrors.NotFound);
|
||||
}
|
||||
|
||||
var shows = await dbContext
|
||||
.Shows.Include(s => s.Genres)
|
||||
.Where(s => showIds.Contains(s.Id))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// Пропавшее шоу не роняет всю операцию: список выбирали галочками, и пока его собирали,
|
||||
// что-то могли удалить. Возвращаем, скольких реально коснулись.
|
||||
foreach (var show in shows)
|
||||
{
|
||||
if (genreIds is not null)
|
||||
ApplyGenres(show, genreIds, command.ReplaceGenres);
|
||||
|
||||
if (command.SetAudience)
|
||||
show.SetAudience(command.Audience);
|
||||
}
|
||||
|
||||
return Result.Success(shows.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Основным остаётся прежний основной жанр — массовая разметка не должна незаметно менять то,
|
||||
/// что показано в списке шоу. Если его не было (жанров не было вовсе), основным станет первый
|
||||
/// из добавленных, как и при обычной правке.
|
||||
/// </summary>
|
||||
private static void ApplyGenres(
|
||||
Domain.Library.Show show,
|
||||
IReadOnlyList<Guid> genreIds,
|
||||
bool replace
|
||||
)
|
||||
{
|
||||
var primary = show.PrimaryGenreId;
|
||||
var ids = replace ? genreIds : show.Genres.Select(g => g.GenreId).Union(genreIds).ToList();
|
||||
show.SetGenres(ids, ids.Contains(primary ?? Guid.Empty) ? primary : null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace TeleWave.Application.Library.BulkTagShows;
|
||||
|
||||
public sealed class BulkTagShowsCommandValidator : AbstractValidator<BulkTagShowsCommand>
|
||||
{
|
||||
public BulkTagShowsCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.ShowIds).NotEmpty();
|
||||
// Команда, которая ничего не меняет, — почти наверняка недосмотр вызывающей стороны.
|
||||
RuleFor(x => x)
|
||||
.Must(x => x.GenreIds is not null || x.SetAudience)
|
||||
.WithMessage("Нечего применять: не заданы ни жанры, ни рейтинг.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user