| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495 |
- using Application.Abstractions.Data;
- using Microsoft.EntityFrameworkCore;
- namespace Application.Abstractions.Channels;
- /// <summary>
- /// URL 파라미터(@handle 또는 YouTube Channel ID)를 내부 Channel.SID 로 변환하는 헬퍼
- /// - `@` 로 시작하면 현재 핸들 → SID 로 조회
- /// - 그 외에는 SID 로 간주 (YouTube Channel ID 24자)
- /// - 현재 핸들로 일치하는 채널이 없으면 이전 핸들 이력 조회 후 현재 핸들/SID 반환
- /// </summary>
- public static class ChannelIdentifierResolver
- {
- public enum IdentifierKind
- {
- Handle,
- Sid
- }
- /// <summary>
- /// <paramref name="ChannelSID"/> 가 null 이면 해당 식별자에 대응하는 채널이 없음을 의미.
- /// <paramref name="CanonicalHandle"/> 이 입력 핸들과 다르면 이전 핸들에서 변경된 채널임을 의미 (리다이렉트 대상).
- /// </summary>
- public readonly record struct ResolveResult(
- string? ChannelSID,
- string? CanonicalHandle,
- IdentifierKind Kind
- );
- public static IdentifierKind DetectKind(string identifier)
- {
- if (!string.IsNullOrEmpty(identifier) && identifier[0] == '@')
- {
- return IdentifierKind.Handle;
- }
- return IdentifierKind.Sid;
- }
- public static string NormalizeHandle(string identifier)
- {
- if (string.IsNullOrEmpty(identifier))
- {
- return string.Empty;
- }
- return identifier[0] == '@' ? identifier[1..] : identifier;
- }
- /// <summary>
- /// 입력 식별자를 Channel.SID 로 정규화.
- /// - SID 입력: DB 조회 없이 그대로 반환 (존재 여부는 후속 Handler 가 검증)
- /// - Handle 입력: 현재 핸들과 일치하는 채널 조회, 없으면 이력 테이블 조회하여 현재 핸들 반환
- /// </summary>
- public static async Task<ResolveResult> ResolveAsync(IAppDbContext db, string identifier, CancellationToken ct)
- {
- if (string.IsNullOrWhiteSpace(identifier))
- {
- return new ResolveResult(null, null, IdentifierKind.Sid);
- }
- var kind = DetectKind(identifier);
- if (kind == IdentifierKind.Sid)
- {
- return new ResolveResult(identifier, null, IdentifierKind.Sid);
- }
- var handle = NormalizeHandle(identifier);
- var current = await db.Channel.AsNoTracking()
- .Where(c => c.Handle == handle)
- .Select(c => new { c.SID, c.Handle })
- .FirstOrDefaultAsync(ct);
- if (current is not null)
- {
- return new ResolveResult(current.SID, current.Handle, IdentifierKind.Handle);
- }
- // 핸들 변경 이력 조회 — 이전 핸들이면 현재 핸들/SID 반환
- var historical = await db.ChannelHandleHistory.AsNoTracking()
- .Where(h => h.OldHandle == handle)
- .OrderByDescending(h => h.ChangedAt)
- .Select(h => new { h.Channel.SID, h.Channel.Handle })
- .FirstOrDefaultAsync(ct);
- if (historical is not null)
- {
- return new ResolveResult(historical.SID, historical.Handle, IdentifierKind.Handle);
- }
- return new ResolveResult(null, null, IdentifierKind.Handle);
- }
- }
|