diff --git a/SPMS.Application/Services/TagService.cs b/SPMS.Application/Services/TagService.cs index 13618af..10967c9 100644 --- a/SPMS.Application/Services/TagService.cs +++ b/SPMS.Application/Services/TagService.cs @@ -1,3 +1,4 @@ +using System.Text.Json; using SPMS.Application.DTOs.Notice; using SPMS.Application.DTOs.Tag; using SPMS.Application.Interfaces; @@ -136,7 +137,30 @@ public class TagService : ITagService if (tag == null) throw new SpmsException(ErrorCodes.TagNotFound, "태그를 찾을 수 없습니다.", 404); - _tagRepository.Delete(tag); - await _unitOfWork.SaveChangesAsync(); + // 트랜잭션으로 원자성 보장 (태그 삭제 + 디바이스 orphan 참조 제거) + using var tx = await _unitOfWork.BeginTransactionAsync(); + try + { + // 해당 태그를 참조하는 디바이스의 Tags에서 tagId 제거 + var devices = await _deviceRepository.GetDevicesByTagIdAsync(request.TagId); + foreach (var device in devices) + { + var tagList = JsonSerializer.Deserialize>(device.Tags!) ?? new(); + tagList.Remove((int)request.TagId); + device.Tags = tagList.Count > 0 ? JsonSerializer.Serialize(tagList) : null; + device.UpdatedAt = DateTime.UtcNow; + _deviceRepository.Update(device); + } + + _tagRepository.Delete(tag); + + await _unitOfWork.SaveChangesAsync(); + await _unitOfWork.CommitTransactionAsync(); + } + catch + { + await _unitOfWork.RollbackTransactionAsync(); + throw; + } } } diff --git a/SPMS.Domain/Interfaces/IDeviceRepository.cs b/SPMS.Domain/Interfaces/IDeviceRepository.cs index 63d7a8f..2cbf623 100644 --- a/SPMS.Domain/Interfaces/IDeviceRepository.cs +++ b/SPMS.Domain/Interfaces/IDeviceRepository.cs @@ -20,4 +20,5 @@ public interface IDeviceRepository : IRepository bool? isActive = null, List? tags = null, string? keyword = null, bool? marketingAgreed = null); Task> GetDeviceCountsByTagIdsAsync(IEnumerable tagIds); + Task> GetDevicesByTagIdAsync(long tagId); } diff --git a/SPMS.Infrastructure/Persistence/Repositories/DeviceRepository.cs b/SPMS.Infrastructure/Persistence/Repositories/DeviceRepository.cs index 6a67e6e..8598518 100644 --- a/SPMS.Infrastructure/Persistence/Repositories/DeviceRepository.cs +++ b/SPMS.Infrastructure/Persistence/Repositories/DeviceRepository.cs @@ -129,6 +129,14 @@ public class DeviceRepository : Repository, IDeviceRepository .ToListAsync(); } + public async Task> GetDevicesByTagIdAsync(long tagId) + { + var tagStr = tagId.ToString(); + return await _dbSet + .Where(d => d.Tags != null && EF.Functions.Like(d.Tags, $"%{tagStr}%")) + .ToListAsync(); + } + public async Task> GetDeviceCountsByTagIdsAsync(IEnumerable tagIds) { var tagIdList = tagIds.ToList();