Handler.cs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. using Application.Abstractions.Crypto;
  2. using Application.Abstractions.Data;
  3. using Application.Abstractions.Messaging;
  4. using Microsoft.EntityFrameworkCore;
  5. using SharedKernel.Results;
  6. using MemberAddressEntity = Domain.Entities.Store.MemberAddress;
  7. namespace Application.Features.Api.MyPage.Address.Save;
  8. public sealed class Handler(IAppDbContext db, IFieldEncryptor encryptor) : ICommandHandler<Command, Result<int>>
  9. {
  10. private const int MaxAddressesPerMember = 5;
  11. public async Task<Result<int>> Handle(Command request, CancellationToken ct)
  12. {
  13. // 입력 검증
  14. if (string.IsNullOrWhiteSpace(request.RecipientName))
  15. {
  16. return Result.Failure<int>(Error.Problem("Address.RecipientRequired", "수령인명은 필수입니다."));
  17. }
  18. if (string.IsNullOrWhiteSpace(request.Phone))
  19. {
  20. return Result.Failure<int>(Error.Problem("Address.PhoneRequired", "연락처는 필수입니다."));
  21. }
  22. if (string.IsNullOrWhiteSpace(request.ZipCode) || request.ZipCode.Length > 10)
  23. {
  24. return Result.Failure<int>(Error.Problem("Address.InvalidZipCode", "우편번호가 잘못되었습니다."));
  25. }
  26. if (string.IsNullOrWhiteSpace(request.Address1) || string.IsNullOrWhiteSpace(request.Address2))
  27. {
  28. return Result.Failure<int>(Error.Problem("Address.RequiredFields", "주소를 정확히 입력해주세요."));
  29. }
  30. // 암호화
  31. var recipientEnc = encryptor.Encrypt(request.RecipientName);
  32. var recipientHash = encryptor.Hash(request.RecipientName);
  33. var phoneEnc = encryptor.Encrypt(request.Phone);
  34. var addr1Enc = encryptor.Encrypt(request.Address1);
  35. var addr2Enc = encryptor.Encrypt(request.Address2);
  36. var keyVersion = encryptor.CurrentKeyVersion;
  37. // IsDefault 처리: true 라면 다른 주소의 IsDefault 모두 false 로
  38. if (request.IsDefault)
  39. {
  40. var others = await db.MemberAddress
  41. .Where(a => a.MemberID == request.MemberID && a.IsDefault)
  42. .ToListAsync(ct);
  43. foreach (var o in others)
  44. {
  45. if (request.ID.HasValue && o.ID == request.ID.Value)
  46. {
  47. continue;
  48. }
  49. o.UnsetDefault();
  50. }
  51. }
  52. if (!request.ID.HasValue || request.ID.Value == 0)
  53. {
  54. // 신규 — 회원당 최대 개수 검증
  55. var count = await db.MemberAddress.CountAsync(a => a.MemberID == request.MemberID, ct);
  56. if (count >= MaxAddressesPerMember)
  57. {
  58. return Result.Failure<int>(Error.Problem("Address.MaxExceeded", $"주소는 회원당 최대 {MaxAddressesPerMember}개까지 등록 가능합니다."));
  59. }
  60. // 첫 주소는 자동 default
  61. var isDefault = request.IsDefault || count == 0;
  62. var newAddress = MemberAddressEntity.Create(
  63. memberID: request.MemberID,
  64. recipientNameEncrypted: recipientEnc,
  65. recipientNameHash: recipientHash,
  66. phoneEncrypted: phoneEnc,
  67. zipCode: request.ZipCode,
  68. address1Encrypted: addr1Enc,
  69. address2Encrypted: addr2Enc,
  70. keyVersion: keyVersion,
  71. isDefault: isDefault
  72. );
  73. db.MemberAddress.Add(newAddress);
  74. await db.SaveChangesAsync(ct);
  75. return Result.Success(newAddress.ID);
  76. }
  77. // 수정
  78. var existing = await db.MemberAddress
  79. .FirstOrDefaultAsync(a => a.ID == request.ID.Value && a.MemberID == request.MemberID, ct);
  80. if (existing is null)
  81. {
  82. return Result.Failure<int>(Error.NotFound("Address.NotFound", "수정할 주소를 찾을 수 없습니다."));
  83. }
  84. existing.Update(
  85. recipientNameEncrypted: recipientEnc,
  86. recipientNameHash: recipientHash,
  87. phoneEncrypted: phoneEnc,
  88. zipCode: request.ZipCode,
  89. address1Encrypted: addr1Enc,
  90. address2Encrypted: addr2Enc,
  91. keyVersion: keyVersion
  92. );
  93. if (request.IsDefault && !existing.IsDefault)
  94. {
  95. existing.SetAsDefault();
  96. }
  97. else if (!request.IsDefault && existing.IsDefault)
  98. {
  99. existing.UnsetDefault();
  100. }
  101. await db.SaveChangesAsync(ct);
  102. return Result.Success(existing.ID);
  103. }
  104. }