Handler.cs 1.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
  1. using Application.Abstractions.Data;
  2. using Application.Abstractions.Messaging;
  3. using Microsoft.EntityFrameworkCore;
  4. using System.Security.Cryptography;
  5. namespace Application.Features.Admin.Store.Game.GenerateCode;
  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.Game.AnyAsync(c => c.Code == candidate, ct);
  18. if (!exists)
  19. {
  20. return candidate;
  21. }
  22. }
  23. // 극히 낮은 확률(31^6 ≈ 8.8억 중 10회 모두 충돌)에서만 도달.
  24. // 호출자는 유효성 검증으로 한 번 더 안전망을 갖는다.
  25. return Generate();
  26. }
  27. private static string Generate()
  28. {
  29. var chars = new char[CodeLength];
  30. var bytes = new byte[CodeLength];
  31. RandomNumberGenerator.Fill(bytes);
  32. for (var i = 0; i < CodeLength; i++)
  33. {
  34. chars[i] = Alphabet[bytes[i] % Alphabet.Length];
  35. }
  36. return new string(chars);
  37. }
  38. }