using LiteCqrs; using Microsoft.EntityFrameworkCore; using TeleWave.Application.Common.Interfaces; using TeleWave.Application.Common.Models; using TeleWave.Domain.Programming; namespace TeleWave.Application.Programming.Groups.UpdateGroup; public sealed class UpdateGroupCommandHandler( IAppDbContext dbContext, DynamicGroupResolver dynamicResolver, GroupStatsService stats ) : ICommandHandler { public async Task Handle( UpdateGroupCommand command, CancellationToken cancellationToken ) { var group = await dbContext .Groups.Include(g => g.Items) .FirstOrDefaultAsync(g => g.Id == command.GroupId, cancellationToken); if (group is null) return Result.Failure(GroupErrors.NotFound); group.Rename(command.Name, command.Description); group.SetFilter(command.Filter?.ToJson()); if (command.Mode is { } mode && mode != group.Mode) await SwitchModeAsync(group, mode, cancellationToken); await stats.RecomputeAsync(group, cancellationToken); return Result.Success(); } /// /// Смена режима набора. Домен сбрасывает список позиций — в двух режимах он значит разное, /// — а прикладной слой достраивает то, чего домену знать не положено. /// /// В статическом режиме список и есть состав, поэтому переход туда материализует вычисленный /// правилом: группа не должна опустеть от смены режима — на неё уже ссылаются слоты. /// /// В динамическом состав считает правило, а список хранит только ручные поправки — и начинается /// он пустым. Прежние позиции, перенесённые закреплёнными, отменяли бы сам переход: сузить набор /// правилом было бы нечем. /// private async Task SwitchModeAsync( Domain.Programming.Group group, GroupMode mode, CancellationToken cancellationToken ) { // Состав считаем до смены режима: после неё правило уже применяется к пустому списку. var composition = mode == GroupMode.Static ? await dynamicResolver.ResolveAsync(group, cancellationToken) : []; group.SetMode(mode); foreach (var element in composition) group.AddElement(element.Kind, element.Id); } }