| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 |
- using Application.Abstractions.Data;
- using Application.Abstractions.Messaging;
- using Application.Features.Api.Feed.Shared;
- using Domain.Entities.Feed;
- using Microsoft.EntityFrameworkCore;
- using SharedKernel.Results;
- namespace Application.Features.Api.Feed.Update;
- public sealed class Handler(IAppDbContext db) : ICommandHandler<Command, Result>
- {
- private const int MaxContentLength = 5000;
- public async Task<Result> Handle(Command request, CancellationToken ct)
- {
- if (string.IsNullOrWhiteSpace(request.Content))
- {
- return Result.Failure(Error.Problem("Feed.ContentRequired", "내용을 입력해주세요."));
- }
- var content = request.Content.Trim();
- if (content.Length > MaxContentLength)
- {
- return Result.Failure(Error.Problem("Feed.ContentTooLong", $"내용은 최대 {MaxContentLength}자까지 입력 가능합니다."));
- }
- var post = await db.FeedPost.FirstOrDefaultAsync(p => p.ID == request.PostID && !p.IsDeleted, ct);
- if (post is null)
- {
- return Result.Failure(Error.NotFound("Feed.PostNotFound", "게시글을 찾을 수 없습니다."));
- }
- if (post.MemberID != request.MemberID)
- {
- return Result.Failure(Error.Forbidden("Feed.NotOwner", "수정 권한이 없습니다."));
- }
- post.Content = content;
- post.UpdatedAt = DateTime.UtcNow;
- // ── 태그 재동기화: 기존 FeedPostTag 전부 제거 후 다시 붙임 ──
- var existingTagLinks = await db.FeedPostTag.Where(pt => pt.FeedPostID == post.ID).ToListAsync(ct);
- var existingTagIDs = existingTagLinks.Select(l => l.FeedTagID).ToList();
- db.FeedPostTag.RemoveRange(existingTagLinks);
- // 기존 태그 UsageCount 차감
- if (existingTagIDs.Count > 0)
- {
- var oldTags = await db.FeedTag.Where(t => existingTagIDs.Contains(t.ID)).ToListAsync(ct);
- foreach (var tag in oldTags)
- {
- tag.UsageCount = tag.UsageCount > 0 ? tag.UsageCount - 1 : 0;
- tag.UpdatedAt = DateTime.UtcNow;
- }
- }
- var hashtagsFromContent = FeedContent.ExtractHashtags(content);
- var mergedTags = new List<string>(hashtagsFromContent);
- if (request.Tags is { Count: > 0 })
- {
- foreach (var t in request.Tags)
- {
- if (!string.IsNullOrWhiteSpace(t) && !mergedTags.Any(x => string.Equals(x, t, StringComparison.OrdinalIgnoreCase)))
- {
- mergedTags.Add(t.Trim());
- }
- }
- }
- byte tagCount = 0;
- foreach (var tagName in mergedTags)
- {
- if (string.IsNullOrWhiteSpace(tagName))
- {
- continue;
- }
- var name = tagName.Trim();
- var slug = name.ToLowerInvariant().Replace(' ', '-');
- var tag = await db.FeedTag.FirstOrDefaultAsync(t => t.Name == name, ct);
- if (tag is null)
- {
- tag = new FeedTag { Name = name, Slug = slug };
- await db.FeedTag.AddAsync(tag, ct);
- await db.SaveChangesAsync(ct);
- }
- tag.UsageCount++;
- tag.UpdatedAt = DateTime.UtcNow;
- await db.FeedPostTag.AddAsync(new FeedPostTag
- {
- FeedPostID = post.ID,
- FeedTagID = tag.ID
- }, ct);
- tagCount++;
- }
- post.Tags = tagCount;
- await db.SaveChangesAsync(ct);
- return Result.Success();
- }
- }
|