using Application.Abstractions.Data; using MediatR; using Microsoft.EntityFrameworkCore; using Domain.Entities.Page.Faq; namespace Application.Features.Faq.Category.Save { public sealed class Handler(IAppDbContext db) : IRequestHandler { public async Task Handle(Command request, CancellationToken ct) { var items = request.Items; var dbRows = await db.FaqCategory.ToListAsync(ct); var dbByID = dbRows.ToDictionary(c => c.ID); // 값이 있는 것만 추출 var requestIDs = items.Where(c => c.ID.HasValue && c.ID.Value > 0).Select(c => c.ID!.Value).ToHashSet(); // 삭제 대상 조회 var deleteTargets = dbRows.Where(x => !requestIDs.Contains(x.ID)).ToList(); if (deleteTargets.Count > 0) { var deleteIDs = deleteTargets.Select(c => c.ID).ToList(); var hasItems = await db.FaqItem.AsNoTracking().AnyAsync(c => deleteIDs.Contains(c.CategoryID), ct); if (hasItems) { throw new InvalidOperationException("FAQ가 등록된 분류는 삭제할 수 없습니다. 먼저 해당 FAQ를 삭제하세요."); } db.FaqCategory.RemoveRange(deleteTargets); } ushort inserted = 0; ushort updated = 0; ushort deleted = 0; foreach(var row in items) { // 신규 추가 if (!row.ID.HasValue || row.ID.Value <= 0) { // Code 중복 검사 if (await db.FaqCategory.AnyAsync(c => c.Code == row.Code && c.ID != row.ID, ct)) { throw new InvalidOperationException($"`{row.Code}`는 이미 등록되었습니다."); } db.FaqCategory.Add( FaqCategory.Create(row.Code, row.Subject, row.Order, row.IsActive) ); inserted++; continue; } // 수정 if (!dbByID.TryGetValue(row.ID.Value, out var existing)) { throw new InvalidOperationException($"존재하지 않는 ID: {row.ID.Value}"); } // Code가 변경된 경우에만 중복 검사 if (existing.Code != row.Code && await db.FaqCategory.AnyAsync(c => c.Code == row.Code, ct)) { throw new InvalidOperationException($"`{row.Code}`는 이미 등록되었습니다."); } existing.Update(row.Code, row.Subject, row.Order, row.IsActive); updated++; } deleted = (ushort)deleteTargets.Count; await db.SaveChangesAsync(ct); return new Response(inserted, updated, deleted); } } }