using Application.Abstractions.Data; using Application.Abstractions.Messaging; using Domain.Entities.Members.Logs; using Domain.Entities.Members.ValueObject; using Domain.Entities.Store.ValueObject; using Microsoft.EntityFrameworkCore; using SharedKernel.Results; namespace Application.Features.Api.Member.Items.Use; internal sealed class Handler(IAppDbContext db) : ICommandHandler> { public async Task> Handle(Command request, CancellationToken ct) { var now = DateTime.UtcNow; var item = await db.MemberItem.FirstOrDefaultAsync(i => i.ID == request.MemberItemID, ct); if (item is null || item.MemberID != request.MemberID) { return Result.Failure(Error.NotFound("MemberItem.NotFound", "보유 아이템을 찾을 수 없습니다.")); } if (!item.IsAvailable) { return Result.Failure(Error.Conflict("MemberItem.Unavailable", "이미 사용했거나 회수된 아이템입니다.")); } string detail; switch (item.Kind) { case ItemKind.ChatBanLift: { var bans = await db.ChatBan .Where(b => b.MemberID == request.MemberID && (b.ExpiresAt == null || b.ExpiresAt > now)) .ToListAsync(ct); if (bans.Count == 0) { return Result.Failure(Error.Problem("MemberItem.NoActiveBan", "해제할 채팅 제재가 없습니다.")); } foreach (var ban in bans) { ban.Lift(now); } detail = $"채팅 제재 {bans.Count}건 해제"; break; } case ItemKind.PostReset: { var affected = await db.Post .Where(p => p.MemberID == request.MemberID && !p.IsDeleted && p.Reports == 0 && p.Comments == 0) .ExecuteUpdateAsync(s => s .SetProperty(p => p.IsDeleted, true) .SetProperty(p => p.DeletedAt, now), ct); if (affected == 0) { return Result.Failure(Error.Problem("MemberItem.NoResettablePost", "초기화할 글이 없습니다. (신고·댓글 없는 본인 글만 대상)")); } detail = $"예전 글 {affected}건 초기화"; break; } case ItemKind.ExpBooster: { detail = $"경험치 부스터 {item.DurationDays}일 활성화"; break; } default: return Result.Failure(Error.Problem("MemberItem.NotDirectlyUsable", "이 아이템은 해당 기능(쪽지 보내기·프로필 변경) 이용 시 자동으로 사용됩니다.")); } item.MarkUsed(now); db.MemberItemUsageLog.Add(MemberItemUsageLog.Create(item.MemberID, item.ID, item.ProductID, item.Kind, MemberItemUsageAction.Used, detail)); await db.SaveChangesAsync(ct); return Result.Success(new Response(item.Kind, detail)); } }