| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- using Application.Abstractions.Data;
- using Application.Abstractions.Messaging;
- using Microsoft.EntityFrameworkCore;
- using SharedKernel.Results;
- using SharedKernel.Storage;
- using Domain.Entities.Store;
- using GameEntity = Domain.Entities.Store.Game;
- namespace Application.Features.Admin.Store.Game.Create;
- public sealed class Handler(IAppDbContext db, IFileStorage fileStorage) : ICommandHandler<Command, Result<int>>
- {
- private static readonly string[] AllowedImageExt = [".jpg", ".jpeg", ".png", ".gif", ".webp"];
- public async Task<Result<int>> Handle(Command request, CancellationToken ct)
- {
- // 커미션 검증 (Domain 검증 보강 — 사전 차단)
- if (request.CommissionRate < 0 || request.CommissionRate > 100)
- {
- return Result.Failure<int>(Error.Problem("Game.InvalidCommissionRate", "게임사 수익률은 0~100% 사이여야 합니다."));
- }
- var code = NormalizeOptional(request.Code);
- var engName = NormalizeOptional(request.EngName);
- var link = NormalizeOptional(request.Link);
- // 한글명 중복 차단
- if (await db.Game.AnyAsync(c => c.KorName == request.KorName, ct))
- {
- return Result.Failure<int>(Error.Conflict("Game.DuplicateKorName", "이미 같은 한글명의 게임이 등록되어 있습니다."));
- }
- // 게임 코드 중복 차단 (입력 시에만)
- if (code is not null && await db.Game.AnyAsync(c => c.Code == code, ct))
- {
- return Result.Failure<int>(Error.Conflict("Game.DuplicateCode", "이미 같은 코드의 게임이 등록되어 있습니다."));
- }
- var game = GameEntity.Create(
- korName: request.KorName,
- publisher: request.Publisher,
- commissionRate: request.CommissionRate,
- code: code,
- engName: engName,
- description: request.Description,
- link: link,
- order: request.Order
- );
- if (!request.IsActive)
- {
- game.Deactivate();
- }
- if (request.ApiCommissionRate < 0 || request.ApiCommissionRate > 100)
- {
- return Result.Failure<int>(Error.Problem("Game.InvalidApiCommissionRate", "API 수수료율은 0~100% 사이여야 합니다."));
- }
- game.SetApiCommissionRate(request.ApiCommissionRate);
- game.SetDetail(request.TrailerUrl, request.MinSpec, request.RecommendedSpec, request.ReleaseDate);
- db.Game.Add(game);
- await db.SaveChangesAsync(ct);
- // 이미지 업로드 (ID 확보 후) — 작은 이미지 + 큰 이미지
- string? thumbnailUrl = null;
- string? bigImageUrl = null;
- if (request.ThumbnailFile is not null)
- {
- var uploadPath = new FileStoragePath(UploadTarget.Upload, UploadFolder.StoreGame, game.ID);
- var saved = await fileStorage.SaveFileAsync(request.ThumbnailFile, uploadPath, AllowedImageExt, ct);
- thumbnailUrl = saved?.Url;
- }
- if (request.BigImageFile is not null)
- {
- var uploadPath = new FileStoragePath(UploadTarget.Upload, UploadFolder.StoreGame, game.ID);
- var saved = await fileStorage.SaveFileAsync(request.BigImageFile, uploadPath, AllowedImageExt, ct);
- bigImageUrl = saved?.Url;
- }
- if (thumbnailUrl is not null || bigImageUrl is not null)
- {
- game.Update(
- korName: game.KorName,
- publisher: game.Publisher,
- commissionRate: game.CommissionRate,
- code: game.Code,
- engName: game.EngName,
- thumbnail: thumbnailUrl ?? game.Thumbnail,
- bigImage: bigImageUrl ?? game.BigImage,
- description: game.Description,
- link: game.Link,
- order: game.Order,
- isActive: game.IsActive
- );
- await db.SaveChangesAsync(ct);
- }
- await SyncDetailAsync(game.ID, request, ct);
- return Result.Success(game.ID);
- }
- private async Task SyncDetailAsync(int gameID, Command request, CancellationToken ct)
- {
- var changed = false;
- if (request.GenreIDs is { Count: > 0 } genreIds)
- {
- var validIds = await db.GameGenre.Where(g => genreIds.Contains(g.ID)).Select(g => g.ID).ToListAsync(ct);
- foreach (var id in validIds)
- {
- db.GameGenreMap.Add(GameGenreMap.Create(gameID, id));
- changed = true;
- }
- }
- if (request.CategoryIDs is { Count: > 0 } categoryIds)
- {
- var validIds = await db.GameCategory.Where(c => categoryIds.Contains(c.ID)).Select(c => c.ID).ToListAsync(ct);
- foreach (var id in validIds)
- {
- db.GameCategoryMap.Add(GameCategoryMap.Create(gameID, id));
- changed = true;
- }
- }
- if (request.DetailImages is { Count: > 0 })
- {
- var uploadPath = new FileStoragePath(UploadTarget.Upload, UploadFolder.StoreGameDetail, gameID);
- var order = 0;
- foreach (var image in request.DetailImages)
- {
- var saved = await fileStorage.SaveFileAsync(image, uploadPath, AllowedImageExt, ct);
- if (saved is null)
- {
- continue;
- }
- db.GameImage.Add(new GameImage
- {
- GameID = gameID,
- FileName = image.FileName,
- HashedName = saved.FileName,
- Path = uploadPath.ToRelativePath(),
- Url = saved.Url,
- Extension = saved.Extension,
- ContentType = image.ContentType,
- Size = saved.Size,
- Width = saved.Width,
- Height = saved.Height,
- Order = order++
- });
- changed = true;
- }
- }
- if (changed)
- {
- await db.SaveChangesAsync(ct);
- }
- }
- private static string? NormalizeOptional(string? value)
- {
- return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
- }
- }
|