| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- using Application.Abstractions.Messaging;
- using Application.Abstractions.Data;
- using SharedKernel.Storage;
- using Microsoft.EntityFrameworkCore;
- namespace Application.Features.Admin.Forum.Post.Update;
- public sealed class Handler(IAppDbContext db, IFileStorage fileStorage) : ICommandHandler<Command>
- {
- private static readonly string[] AllowedImageExtensions = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"];
- private static readonly string[] AllowedFileExtensions = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".txt", ".zip", ".rar", ".7z", ".hwp", ".hwpx", ".csv"];
- public async Task Handle(Command request, CancellationToken ct)
- {
- var post = await db.Post.FirstOrDefaultAsync(x => x.ID == request.ID, ct);
- if (post is null)
- {
- throw new KeyNotFoundException("게시글을 찾을 수 없습니다.");
- }
- post.BoardPrefixID = request.BoardPrefixID;
- post.Subject = request.Subject;
- post.Content = request.Content ?? string.Empty;
- post.IsNotice = request.IsNotice;
- post.IsSecret = request.IsSecret;
- post.IsAnonymous = request.IsAnonymous;
- post.UpdatedAt = DateTime.UtcNow;
- // 썸네일 처리
- if (request.ThumbnailFile is not null)
- {
- var uploadPath = new FileStoragePath(UploadTarget.Upload, UploadFolder.Post, post.ID);
- if (!string.IsNullOrEmpty(post.Thumbnail))
- {
- fileStorage.DeleteByUrl(post.Thumbnail);
- }
- var result = await fileStorage.SaveFileAsync(request.ThumbnailFile, uploadPath, AllowedImageExtensions, ct);
- post.Thumbnail = result?.Url;
- }
- // 파일 처리 (새 파일 추가)
- if (request.Files is { Count: > 0 })
- {
- var uploadPath = new FileStoragePath(UploadTarget.Upload, UploadFolder.Post, post.ID);
- foreach (var file in request.Files)
- {
- var result = await fileStorage.SaveFileAsync(file, uploadPath, AllowedFileExtensions, ct);
- if (result is not null)
- {
- var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
- await db.PostFile.AddAsync(new Domain.Entities.Forum.Posts.PostFile
- {
- BoardID = post.BoardID,
- PostID = post.ID,
- UUID = Guid.NewGuid(),
- FileName = file.FileName,
- HashedName = result.FileName,
- Path = uploadPath.ToRelativePath(),
- Url = result.Url,
- Extension = ext,
- ContentType = file.ContentType,
- Size = result.Size
- }, ct);
- }
- }
- var fileCount = await db.PostFile.CountAsync(x => x.PostID == post.ID && !x.IsDisabled, ct);
- post.Files = (byte)Math.Min(fileCount + request.Files.Count, byte.MaxValue);
- }
- // 태그 처리 (diff 기반)
- if (request.Tags is not null)
- {
- var existingPostTags = await db.PostTag.Include(x => x.Tag).Where(x => x.PostID == post.ID).ToListAsync(ct);
- var newTagSlugs = request.Tags.Where(t => !string.IsNullOrWhiteSpace(t)).Select(t => t.Trim().ToLowerInvariant().Replace(' ', '-')).ToHashSet();
- var existingSlugs = existingPostTags.Select(x => x.Tag.Slug).ToHashSet();
- // 삭제할 태그: 기존에 있지만 새 목록에 없는 것
- var toRemove = existingPostTags.Where(x => !newTagSlugs.Contains(x.Tag.Slug)).ToList();
- foreach (var postTag in toRemove)
- {
- db.PostTag.Remove(postTag);
- if (postTag.Tag.UsageCount > 0)
- {
- postTag.Tag.UsageCount--;
- postTag.Tag.UpdatedAt = DateTime.UtcNow;
- }
- }
- // 추가할 태그: 새 목록에 있지만 기존에 없는 것
- var toAdd = request.Tags
- .Where(t => !string.IsNullOrWhiteSpace(t))
- .Select(t => t.Trim())
- .Where(t => !existingSlugs.Contains(t.ToLowerInvariant().Replace(' ', '-')))
- .ToList();
- foreach (var tagName in toAdd)
- {
- var slug = tagName.ToLowerInvariant().Replace(' ', '-');
- var tag = await db.Tag.FirstOrDefaultAsync(x => x.Slug == slug, ct);
- if (tag is null)
- {
- tag = new Domain.Entities.Forum.Posts.Tag
- {
- Name = tagName,
- Slug = slug
- };
- await db.Tag.AddAsync(tag, ct);
- await db.SaveChangesAsync(ct);
- }
- tag.UsageCount++;
- tag.UpdatedAt = DateTime.UtcNow;
- await db.PostTag.AddAsync(new Domain.Entities.Forum.Posts.PostTag
- {
- BoardID = post.BoardID,
- PostID = post.ID,
- TagID = tag.ID
- }, ct);
- }
- post.Tags = (byte)Math.Min(newTagSlugs.Count, byte.MaxValue);
- }
- await db.SaveChangesAsync(ct);
- }
- }
|