| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- using Application.Abstractions.Data;
- using Application.Abstractions.Messaging;
- using Application.Abstractions.YouTube;
- using Domain.Entities.Members;
- using Microsoft.EntityFrameworkCore;
- namespace Application.Features.Api.Studio.ConnectYouTube;
- internal sealed class Handler(
- IAppDbContext db,
- IGoogleOAuthService oauthService,
- IYouTubeApiService youtubeApiService,
- IYouTubeChannelCache channelCache
- ) : ICommandHandler<Command>
- {
- public async Task Handle(Command request, CancellationToken ct)
- {
- var config = await db.Config.AsNoTracking().OrderByDescending(x => x.ID).FirstOrDefaultAsync(ct);
- if (config is null
- || string.IsNullOrEmpty(config.External.GoogleClientId)
- || string.IsNullOrEmpty(config.External.GoogleClientSecretEnc
- )) {
- throw new InvalidOperationException("Google OAuth 설정이 없습니다.");
- }
- var tokens = await oauthService.ExchangeCodeAsync(
- request.Code,
- request.RedirectUri,
- config.External.GoogleClientId,
- config.External.GoogleClientSecretEnc,
- ct
- );
- if (tokens is null)
- {
- throw new InvalidOperationException("Google 인증 코드 교환에 실패했습니다.");
- }
- var ytInfo = await youtubeApiService.GetMyChannelAsync(tokens.AccessToken, ct);
- if (ytInfo is null)
- {
- throw new InvalidOperationException("YouTube 채널 정보를 가져올 수 없습니다.");
- }
- var youtubeUrl = ytInfo.CustomUrl is not null
- ? $"https://youtube.com/{ytInfo.CustomUrl}"
- : $"https://youtube.com/channel/{ytInfo.ChannelID}";
- // Member 크리에이터 전환
- var member = await db.Member.FirstAsync(m => m.ID == request.MemberID, ct);
- if (!member.IsCreator)
- {
- member.SetCreator(true);
- }
- // Channel 생성 또는 업데이트
- var channel = await db.Channel.FirstOrDefaultAsync(c => c.MemberID == request.MemberID, ct);
- if (channel is null)
- {
- channel = Domain.Entities.Members.Channel.Create(request.MemberID, ytInfo.ChannelID, ytInfo.Title, youtubeUrl);
- db.Channel.Add(channel);
- }
- // YouTube 상세 정보 DB 저장
- channel.UpdateYouTubeInfo(
- ytInfo.ChannelID,
- ytInfo.Title,
- ytInfo.CustomUrl,
- ytInfo.Description,
- ytInfo.ThumbnailUrl,
- ytInfo.BannerUrl,
- ytInfo.SubscriberCount,
- ytInfo.VideoCount,
- ytInfo.ViewCount,
- ytInfo.Email,
- ytInfo.PublishedAt
- );
- // MemberOAuthToken 생성 또는 업데이트 (Provider = "YouTube")
- var oauthToken = await db.MemberOAuthToken.FirstOrDefaultAsync(t => t.MemberID == request.MemberID && t.Provider == "YouTube", ct);
- var scopesStr = string.Join(" ", tokens.Scopes);
- if (oauthToken is null)
- {
- oauthToken = MemberOAuthToken.Create(
- request.MemberID,
- "YouTube",
- tokens.AccessToken,
- tokens.RefreshToken,
- tokens.ExpiresAt,
- scopesStr
- );
- db.MemberOAuthToken.Add(oauthToken);
- }
- else
- {
- oauthToken.UpdateTokens(tokens.AccessToken, tokens.RefreshToken, tokens.ExpiresAt, scopesStr);
- }
- await db.SaveChangesAsync(ct);
- // 캐시 갱신
- await channelCache.SetAsync(ytInfo);
- }
- }
|