improvement: 태그 삭제 시 디바이스 orphan 참조 제거 (#186) #246

Merged
seonkyu.kim merged 1 commits from improvement/#186-tag-crud-api into develop 2026-02-26 00:17:18 +00:00
3 changed files with 35 additions and 2 deletions

View File

@ -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);
// 트랜잭션으로 원자성 보장 (태그 삭제 + 디바이스 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<List<int>>(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;
}
}
}

View File

@ -20,4 +20,5 @@ public interface IDeviceRepository : IRepository<Device>
bool? isActive = null, List<int>? tags = null,
string? keyword = null, bool? marketingAgreed = null);
Task<Dictionary<long, int>> GetDeviceCountsByTagIdsAsync(IEnumerable<long> tagIds);
Task<IReadOnlyList<Device>> GetDevicesByTagIdAsync(long tagId);
}

View File

@ -129,6 +129,14 @@ public class DeviceRepository : Repository<Device>, IDeviceRepository
.ToListAsync();
}
public async Task<IReadOnlyList<Device>> 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<Dictionary<long, int>> GetDeviceCountsByTagIdsAsync(IEnumerable<long> tagIds)
{
var tagIdList = tagIds.ToList();