| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100 |
- using Application.Abstractions.Data;
- using Application.Abstractions.Messaging;
- using Microsoft.EntityFrameworkCore;
- using SharedKernel.Results;
- using GameLanguageEntity = Domain.Entities.Store.GameLanguage;
- namespace Application.Features.Admin.Store.GameLanguage.Save;
- public sealed class Handler(IAppDbContext db) : ICommandHandler<Command, Result<Response>>
- {
- public async Task<Result<Response>> Handle(Command request, CancellationToken ct)
- {
- var items = request.Items;
- var dbRows = await db.GameLanguage.ToListAsync(ct);
- var dbByID = dbRows.ToDictionary(c => c.ID);
- var requestIDs = items.Where(c => c.ID.HasValue && c.ID.Value > 0).Select(c => c.ID!.Value).ToHashSet();
- var deleteTargets = dbRows.Where(x => !requestIDs.Contains(x.ID)).ToList();
- if (deleteTargets.Count > 0)
- {
- var deleteIDs = deleteTargets.Select(c => c.ID).ToList();
- var inUse = await db.GameLanguageSupport.AsNoTracking().AnyAsync(c => deleteIDs.Contains(c.GameLanguageID), ct);
- if (inUse)
- {
- return Result.Failure<Response>(Error.Conflict("GameLanguage.InUse", "게임에 연결된 언어는 삭제할 수 없습니다. 먼저 해당 게임에서 언어 선택을 해제하세요."));
- }
- db.GameLanguage.RemoveRange(deleteTargets);
- }
- ushort inserted = 0;
- ushort updated = 0;
- // 이미 사용 중인 한글명(트림 기준) → 소유 ID. 삭제 대상 제외. 배치 내 중복까지 인메모리로 차단 (per-row AnyAsync 제거)
- var deleteSet = deleteTargets.ToHashSet();
- var takenNames = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
- foreach (var x in dbRows)
- {
- if (!deleteSet.Contains(x))
- {
- takenNames[x.KorName.Trim()] = x.ID;
- }
- }
- foreach (var row in items)
- {
- if (string.IsNullOrWhiteSpace(row.KorName))
- {
- return Result.Failure<Response>(Error.Problem("GameLanguage.KorNameRequired", "언어 한글명은 필수입니다."));
- }
- var name = row.KorName.Trim();
- // 신규 추가
- if (!row.ID.HasValue || row.ID.Value <= 0)
- {
- if (takenNames.ContainsKey(name))
- {
- return Result.Failure<Response>(Error.Conflict("GameLanguage.DuplicateKorName", $"`{name}`는 이미 등록된 언어입니다."));
- }
- db.GameLanguage.Add(GameLanguageEntity.Create(row.KorName, row.EngName, row.Order, row.IsActive));
- takenNames[name] = 0; // 배치 내 신규(ID 미정) — 후속 행의 중복 차단용
- inserted++;
- continue;
- }
- // 수정
- if (!dbByID.TryGetValue(row.ID.Value, out var existing))
- {
- return Result.Failure<Response>(Error.NotFound("GameLanguage.NotFound", $"존재하지 않는 ID: {row.ID.Value}"));
- }
- if (takenNames.TryGetValue(name, out var owner) && owner != row.ID.Value)
- {
- return Result.Failure<Response>(Error.Conflict("GameLanguage.DuplicateKorName", $"`{name}`는 이미 등록된 언어입니다."));
- }
- var oldName = existing.KorName.Trim();
- if (!string.Equals(oldName, name, StringComparison.OrdinalIgnoreCase))
- {
- takenNames.Remove(oldName);
- takenNames[name] = row.ID.Value;
- }
- existing.Update(row.KorName, row.EngName, row.Order, row.IsActive);
- updated++;
- }
- var deleted = (ushort)deleteTargets.Count;
- await db.SaveChangesAsync(ct);
- return Result.Success(new Response(inserted, updated, deleted));
- }
- }
|