using Application.Abstractions.Messaging; using Application.Abstractions.Data; using Application.Abstractions.Cache; using Domain.Entities.Crypto; using SharedKernel.Storage; using Microsoft.EntityFrameworkCore; namespace Application.Features.Admin.Crypto.List.Update { public sealed class Handler(IAppDbContext db, IFileStorage fileStorage, ICacheService cache) : ICommandHandler { public async Task Handle(Command request, CancellationToken ct) { var coin = await db.Coin.Include(c => c.CoinCategoryMap).FirstOrDefaultAsync(c => c.ID == request.ID, ct); if (coin is null) { throw new KeyNotFoundException("코인을 찾을 수 없습니다."); } if (await db.Coin.AnyAsync(c => c.Symbol == request.Symbol.ToUpper() && c.ID != request.ID, ct)) { throw new InvalidOperationException($"`{request.Symbol.ToUpper()}`는 이미 등록된 심볼입니다."); } FileStoragePath uploadPath = new FileStoragePath(UploadTarget.Upload, UploadFolder.Crypto, coin.ID); string[] allowedFileExtensions = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg"]; string? logoImagePath = coin.LogoImage; if (request.DeleteLogoImage && !string.IsNullOrEmpty(coin.LogoImage)) { fileStorage.DeleteByUrl(coin.LogoImage); logoImagePath = null; } // 로고 이미지 파일이 제공된 경우 기존 이미지를 삭제하고 새 이미지 저장 if (request.LogoImageFile is not null) { if (!string.IsNullOrEmpty(coin.LogoImage)) { fileStorage.DeleteByUrl(coin.LogoImage); } logoImagePath = (await fileStorage.SaveFileAsync(request.LogoImageFile, uploadPath, allowedFileExtensions, ct))?.Url; } coin.Update( request.Symbol, request.KorName, request.EngName, request.Description, request.ContractAddress, request.WebsiteUrl, request.WhitepaperUrl, request.TwitterUrl, request.TelegramUrl, request.IsActive, request.IsWarning, request.IsNew, request.IsDelisted ); coin.SetLogoImage(logoImagePath); var existingMaps = coin.CoinCategoryMap.ToList(); db.CoinCategoryMap.RemoveRange(existingMaps); foreach (var categoryID in request.CategoryIDs) { db.CoinCategoryMap.Add(new CoinCategoryMap { CoinID = coin.ID, CategoryID = categoryID }); } await db.SaveChangesAsync(ct); await cache.RemoveByPrefixAsync("crypto:", ct); } } }