Handler.cs 1.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. using System.Security.Cryptography;
  2. using Application.Abstractions.Data;
  3. using Application.Abstractions.Messaging;
  4. using Microsoft.EntityFrameworkCore;
  5. namespace Application.Features.Admin.Channel.List.GenerateDonationCode;
  6. internal sealed class Handler(IAppDbContext db) : IQueryHandler<Query, string>
  7. {
  8. // 혼동되는 문자(0/O, 1/I/L) 제외 31자. 6자리 조합 31^6 ≈ 8.8억.
  9. private const string Alphabet = "23456789ABCDEFGHJKMNPQRSTUVWXYZ";
  10. private const int CodeLength = 6;
  11. private const int MaxAttempts = 10;
  12. public async Task<string> Handle(Query request, CancellationToken ct)
  13. {
  14. for (var attempt = 0; attempt < MaxAttempts; attempt++)
  15. {
  16. var candidate = Generate();
  17. var exists = await db.Channel.AnyAsync(c => c.DonationCode == candidate, ct);
  18. if (!exists)
  19. {
  20. return candidate;
  21. }
  22. }
  23. // 31^6 ≈ 8.8억 중 10회 모두 충돌은 극히 낮은 확률. 호출자 측 유효성 검증으로 한 번 더 안전망.
  24. return Generate();
  25. }
  26. private static string Generate()
  27. {
  28. var chars = new char[CodeLength];
  29. var bytes = new byte[CodeLength];
  30. RandomNumberGenerator.Fill(bytes);
  31. for (var i = 0; i < CodeLength; i++)
  32. {
  33. chars[i] = Alphabet[bytes[i] % Alphabet.Length];
  34. }
  35. return new string(chars);
  36. }
  37. }