using Application.Abstractions.Messaging; using Application.Abstractions.Data; using Domain.Entities.Forum.ValueObject; using SharedKernel.Results; using Microsoft.EntityFrameworkCore; namespace Application.Features.Api.Forum.CommentReaction.Toggle; public sealed class Handler(IAppDbContext db) : ICommandHandler { public async Task Handle(Command request, CancellationToken ct) { var comment = await db.Comment.FirstOrDefaultAsync(x => x.ID == request.CommentID, ct); if (comment is null) { return Result.Failure(Error.NotFound("CommentReaction.CommentNotFound", "댓글을 찾을 수 없습니다.")); } var existing = await db.CommentReaction.FirstOrDefaultAsync(x => x.CommentID == request.CommentID && x.MemberID == request.MemberID, ct); // MemberStats 조회 (Like만 카운트) — 1회 쿼리로 giver/receiver 동시 로드 var memberIDs = comment.MemberID != request.MemberID ? new[] { request.MemberID, comment.MemberID } : [request.MemberID]; var statsList = await db.MemberStats.Where(x => memberIDs.Contains(x.MemberID)).ToListAsync(ct); var giverStats = statsList.FirstOrDefault(x => x.MemberID == request.MemberID); var receiverStats = comment.MemberID != request.MemberID ? statsList.FirstOrDefault(x => x.MemberID == comment.MemberID) : null; if (existing is not null) { if (existing.Reaction == request.Reaction) { if (existing.Reaction == Reaction.Like) { comment.Likes--; if (giverStats is not null) { giverStats.LikeGivenCount--; } if (receiverStats is not null) { receiverStats.LikeReceivedCount--; } } else { comment.Dislikes--; } db.CommentReaction.Remove(existing); } else { if (existing.Reaction == Reaction.Like) { comment.Likes--; comment.Dislikes++; // Like → Dislike: Like 카운트 감소 if (giverStats is not null) { giverStats.LikeGivenCount--; } if (receiverStats is not null) { receiverStats.LikeReceivedCount--; } } else { comment.Dislikes--; comment.Likes++; // Dislike → Like: Like 카운트 증가 if (giverStats is not null) { giverStats.LikeGivenCount++; } if (receiverStats is not null) { receiverStats.LikeReceivedCount++; } } existing.Reaction = request.Reaction; existing.UpdatedAt = DateTime.UtcNow; } } else { var reaction = new Domain.Entities.Forum.Comments.CommentReaction { BoardID = comment.BoardID, PostID = comment.PostID, CommentID = request.CommentID, MemberID = request.MemberID, Reaction = request.Reaction }; await db.CommentReaction.AddAsync(reaction, ct); if (request.Reaction == Reaction.Like) { comment.Likes++; if (giverStats is not null) { giverStats.LikeGivenCount++; } if (receiverStats is not null) { receiverStats.LikeReceivedCount++; } } else { comment.Dislikes++; } } await db.SaveChangesAsync(ct); return Result.Success(); } }