Handler.cs 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. using Application.Abstractions.Data;
  2. using Application.Abstractions.Messaging;
  3. using Domain.Entities.Members.Logs;
  4. using Domain.Entities.Members.ValueObject;
  5. using Domain.Entities.Store.ValueObject;
  6. using Microsoft.EntityFrameworkCore;
  7. using SharedKernel.Results;
  8. namespace Application.Features.Api.Member.Items.Use;
  9. internal sealed class Handler(IAppDbContext db) : ICommandHandler<Command, Result<Response>>
  10. {
  11. public async Task<Result<Response>> Handle(Command request, CancellationToken ct)
  12. {
  13. var now = DateTime.UtcNow;
  14. var item = await db.MemberItem.FirstOrDefaultAsync(i => i.ID == request.MemberItemID, ct);
  15. if (item is null || item.MemberID != request.MemberID)
  16. {
  17. return Result.Failure<Response>(Error.NotFound("MemberItem.NotFound", "보유 아이템을 찾을 수 없습니다."));
  18. }
  19. if (!item.IsAvailable)
  20. {
  21. return Result.Failure<Response>(Error.Conflict("MemberItem.Unavailable", "이미 사용했거나 회수된 아이템입니다."));
  22. }
  23. string detail;
  24. switch (item.Kind)
  25. {
  26. case ItemKind.ChatBanLift:
  27. {
  28. var bans = await db.ChatBan
  29. .Where(b => b.MemberID == request.MemberID && (b.ExpiresAt == null || b.ExpiresAt > now))
  30. .ToListAsync(ct);
  31. if (bans.Count == 0)
  32. {
  33. return Result.Failure<Response>(Error.Problem("MemberItem.NoActiveBan", "해제할 채팅 제재가 없습니다."));
  34. }
  35. foreach (var ban in bans)
  36. {
  37. ban.Lift(now);
  38. }
  39. detail = $"채팅 제재 {bans.Count}건 해제";
  40. break;
  41. }
  42. case ItemKind.PostReset:
  43. {
  44. var affected = await db.Post
  45. .Where(p => p.MemberID == request.MemberID && !p.IsDeleted && p.Reports == 0 && p.Comments == 0)
  46. .ExecuteUpdateAsync(s => s
  47. .SetProperty(p => p.IsDeleted, true)
  48. .SetProperty(p => p.DeletedAt, now), ct);
  49. if (affected == 0)
  50. {
  51. return Result.Failure<Response>(Error.Problem("MemberItem.NoResettablePost", "초기화할 글이 없습니다. (신고·댓글 없는 본인 글만 대상)"));
  52. }
  53. detail = $"예전 글 {affected}건 초기화";
  54. break;
  55. }
  56. case ItemKind.ExpBooster:
  57. {
  58. detail = $"경험치 부스터 {item.DurationDays}일 활성화";
  59. break;
  60. }
  61. default:
  62. return Result.Failure<Response>(Error.Problem("MemberItem.NotDirectlyUsable",
  63. "이 아이템은 해당 기능(쪽지 보내기·프로필 변경) 이용 시 자동으로 사용됩니다."));
  64. }
  65. item.MarkUsed(now);
  66. db.MemberItemUsageLog.Add(MemberItemUsageLog.Create(item.MemberID, item.ID, item.ProductID, item.Kind, MemberItemUsageAction.Used, detail));
  67. await db.SaveChangesAsync(ct);
  68. return Result.Success(new Response(item.Kind, detail));
  69. }
  70. }