| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- using Application.Abstractions.Messaging;
- using Application.Abstractions.Data;
- using SharedKernel.Storage;
- using Microsoft.EntityFrameworkCore;
- using System.Text.RegularExpressions;
- namespace Application.Features.Admin.Forum.Post.Create;
- 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"];
- private static readonly Regex ImgBase64Regex = new(@"<img\b[^>]*?\bsrc\s*=\s*""(?<src>data:image/(?<ext>[a-zA-Z0-9.+-]+);base64,(?<data>[a-zA-Z0-9+/=\s]+))""", RegexOptions.Compiled | RegexOptions.IgnoreCase);
- public async Task Handle(Command request, CancellationToken ct)
- {
- if (!await db.Board.AnyAsync(x => x.ID == request.BoardID, ct))
- {
- throw new KeyNotFoundException("게시판을 찾을 수 없습니다.");
- }
- var post = new Domain.Entities.Forum.Posts.Post
- {
- BoardID = request.BoardID,
- BoardPrefixID = request.BoardPrefixID,
- MemberID = null,
- Subject = request.Subject,
- Content = request.Content ?? string.Empty,
- IsNotice = request.IsNotice,
- IsSecret = request.IsSecret,
- IsAnonymous = request.IsAnonymous,
- Name = "관리자"
- };
- await db.Post.AddAsync(post, ct);
- await db.SaveChangesAsync(ct);
- var uploadPath = new FileStoragePath(UploadTarget.Upload, UploadFolder.Post, post.ID);
- // 에디터 이미지 처리 (base64 → 파일 + PostImage 레코드)
- var content = post.Content;
- var matches = ImgBase64Regex.Matches(content);
- if (matches.Count > 0)
- {
- byte imageCount = 0;
- var firstImageUrl = (string?)null;
- foreach (Match match in matches)
- {
- var ext = FileUtils.NormalizeExtension(match.Groups["ext"].Value);
- var bytes = Convert.FromBase64String(match.Groups["data"].Value);
- var result = await fileStorage.SaveBytesAsync(bytes, ext, uploadPath, ct);
- if (result is not null)
- {
- content = content.Replace(match.Groups["src"].Value, result.Url);
- firstImageUrl ??= result.Url;
- await db.PostImage.AddAsync(new Domain.Entities.Forum.Posts.PostImage
- {
- BoardID = request.BoardID,
- PostID = post.ID,
- FileName = result.FileName,
- HashedName = result.FileName,
- Path = uploadPath.ToRelativePath(),
- Url = result.Url,
- Extension = ext,
- ContentType = $"image/{match.Groups["ext"].Value}",
- Size = result.Size,
- Width = result.Width,
- Height = result.Height
- }, ct);
- imageCount++;
- }
- }
- post.Content = content;
- post.Images = imageCount;
- if (string.IsNullOrEmpty(post.Thumbnail) && firstImageUrl is not null)
- {
- post.Thumbnail = firstImageUrl;
- }
- }
- // 썸네일 처리
- if (request.ThumbnailFile is not null)
- {
- var result = await fileStorage.SaveFileAsync(request.ThumbnailFile, uploadPath, AllowedImageExtensions, ct);
- if (result is not null)
- {
- post.Thumbnail = result.Url;
- }
- }
- // 파일 처리
- if (request.Files is { Count: > 0 })
- {
- byte fileCount = 0;
- 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 = request.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);
- fileCount++;
- }
- }
- post.Files = fileCount;
- }
- // 태그 처리
- if (request.Tags is { Count: > 0 })
- {
- byte tagCount = 0;
- foreach (var tagName in request.Tags)
- {
- if (string.IsNullOrWhiteSpace(tagName))
- {
- continue;
- }
- var name = tagName.Trim();
- var slug = name.ToLowerInvariant().Replace(' ', '-');
- var tag = await db.Tag.FirstOrDefaultAsync(x => x.Name == name, ct);
- if (tag is null)
- {
- tag = new Domain.Entities.Forum.Posts.Tag
- {
- Name = name,
- 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 = request.BoardID,
- PostID = post.ID,
- TagID = tag.ID
- }, ct);
- tagCount++;
- }
- post.Tags = tagCount;
- }
- await db.SaveChangesAsync(ct);
- // Board 게시글 카운트 증가
- var board = await db.Board.FirstOrDefaultAsync(x => x.ID == request.BoardID, ct);
- if (board is not null)
- {
- board.Posts++;
- board.UpdatedAt = DateTime.UtcNow;
- // BoardGroup 게시글 카운트 증가
- var boardGroup = await db.BoardGroup.FirstOrDefaultAsync(x => x.ID == board.BoardGroupID, ct);
- if (boardGroup is not null)
- {
- boardGroup.Posts++;
- boardGroup.UpdatedAt = DateTime.UtcNow;
- }
- await db.SaveChangesAsync(ct);
- }
- }
- }
|