| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102 |
- using Application.Abstractions.Data;
- using Application.Abstractions.Messaging;
- using Application.Abstractions.Notification;
- using Domain.Entities.Feed;
- using Domain.Entities.Notifications.ValueObject;
- using Microsoft.EntityFrameworkCore;
- using SharedKernel.Results;
- namespace Application.Features.Api.Feed.CreateComment;
- public sealed class Handler(IAppDbContext db, INotificationService notificationService) : ICommandHandler<Command, Result<Response>>
- {
- private const int MaxContentLength = 2000;
- private const int MaxDepth = 10;
- public async Task<Result<Response>> Handle(Command request, CancellationToken ct)
- {
- if (request.MemberID <= 0)
- {
- return Result.Failure<Response>(Error.Problem("Feed.Comment.MemberRequired", "로그인이 필요합니다."));
- }
- if (string.IsNullOrWhiteSpace(request.Content))
- {
- return Result.Failure<Response>(Error.Problem("Feed.Comment.ContentRequired", "댓글 내용을 입력해주세요."));
- }
- var content = request.Content.Trim();
- if (content.Length > MaxContentLength)
- {
- return Result.Failure<Response>(Error.Problem("Feed.Comment.ContentTooLong", $"댓글은 최대 {MaxContentLength}자까지 입력 가능합니다."));
- }
- var post = await db.FeedPost.FirstOrDefaultAsync(p => p.ID == request.PostID && !p.IsDeleted, ct);
- if (post is null)
- {
- return Result.Failure<Response>(Error.NotFound("Feed.PostNotFound", "게시글을 찾을 수 없습니다."));
- }
- sbyte depth = 0;
- FeedComment? parent = null;
- if (request.ParentCommentID is int parentID && parentID > 0)
- {
- parent = await db.FeedComment.FirstOrDefaultAsync(c => c.ID == parentID && c.FeedPostID == request.PostID, ct);
- if (parent is null)
- {
- return Result.Failure<Response>(Error.NotFound("Feed.ParentCommentNotFound", "부모 댓글을 찾을 수 없습니다."));
- }
- depth = (sbyte)Math.Min(parent.Depth + 1, MaxDepth);
- }
- var comment = new FeedComment
- {
- FeedPostID = request.PostID,
- MemberID = request.MemberID,
- ParentID = parent?.ID,
- Depth = depth,
- Content = content
- };
- await db.FeedComment.AddAsync(comment, ct);
- post.Comments++;
- if (parent is not null)
- {
- parent.Replies++;
- }
- await db.SaveChangesAsync(ct);
- // ── 알림: 부모 댓글 작성자 + 포스트 작성자 (본인 제외, 중복 제외) ──
- var author = await db.Member.AsNoTracking().Where(m => m.ID == request.MemberID).Select(m => new { m.Name, m.SID }).FirstOrDefaultAsync(ct);
- var notifyTargets = new HashSet<int>();
- if (parent is not null && parent.MemberID != request.MemberID)
- {
- notifyTargets.Add(parent.MemberID);
- }
- if (post.MemberID != request.MemberID)
- {
- notifyTargets.Add(post.MemberID);
- }
- foreach (var targetID in notifyTargets)
- {
- await notificationService.SendAsync(
- memberID: targetID,
- type: NotificationType.NewFeedComment,
- title: parent is not null ? "새 답글" : "새 댓글",
- message: $"{author?.Name ?? author?.SID ?? "누군가"}님이 댓글을 남겼습니다.",
- actionUrl: $"/feed/post/{request.PostID}",
- relatedType: "FeedComment",
- relatedID: comment.ID,
- ct: ct
- );
- }
- return new Response(comment.ID);
- }
- }
|