using System.Linq.Expressions; using System.Reflection; using System.Text; using System.Text.Json; using Back.Program.Common.Auth; using Back.Program.Common.Data; using Back.Program.Common.Model; using Back.Program.Models.Entities; using Microsoft.EntityFrameworkCore; namespace Back.Program.Services.V1 { public interface IRepositoryService { // Task ValidateToken(string token, string refresh); Task SaveData(T entity, Expression> key = null) where T : class; Task DeleteData(T entity, Expression> key = null) where T : class; String ReadSummary(Type type, String name); Task SendFrontData(T data, string url); } public class RepositoryService: IRepositoryService { private readonly AppDbContext _dbContext; private readonly ILogger _logger; private readonly JwtTokenService _jwtTokenService; public RepositoryService(AppDbContext dbContext, ILogger logger, JwtTokenService jwtTokenService) { _dbContext = dbContext; _logger = logger; _jwtTokenService = jwtTokenService; } public async Task SaveData(T entity, Expression> key = null) where T : class { try { if (key != null) { // key를 가지고 EF 로 돌리는게 아니라 내가 조건을 넣어서 하는 경우에 사용함 var value = key.Compile()(entity); var parameter = Expression.Parameter(typeof(T), "x"); var invokedExpr = Expression.Invoke(key, parameter); var constantExpr = Expression.Constant(value, key.Body.Type); var equalsExpr = Expression.Equal(invokedExpr, constantExpr); var predicate = Expression.Lambda>(equalsExpr, parameter); var entityData = await _dbContext.Set().FirstOrDefaultAsync(predicate); if (entityData != null) { _logger.LogInformation($"[{typeof(T)}] 해당 PK 존재 = [{value}]: 계속"); _dbContext.Entry(entityData).CurrentValues.SetValues(entity); if (!(_dbContext.Entry(entityData).Properties.Any(p => p.IsModified))) { _logger.LogInformation($"[{typeof(T)}] 변경 사항 없음"); return true; } _logger.LogInformation($"[{typeof(T)}] 변경 사항이 존재"); } else { _logger.LogInformation($"[{typeof(T)}] 처음등록"); _dbContext.Set().Add(entity); } } else { // EF 로 직접 키를 잡아서 사용 (관계키나 이런거 할때도 노상관됨) // 모델이 존재하지 않거나 기본 키 정의가 되지 않은 오류가 발생할 건데 그건 운영 단계에서는 오류 나면 안되는거니 var keyProperties = _dbContext.Model.FindEntityType(typeof(T)).FindPrimaryKey().Properties; // 각 키 속성에 대해, entity에서 실제 키 값을 추출 var keyValues = keyProperties.Select(p => typeof(T).GetProperty(p.Name).GetValue(entity)).ToArray(); // 기본 키 값을 이용해서 기존 엔티티를 찾음 (복합 키도 자동으로 처리됨) var existingEntity = await _dbContext.Set().FindAsync(keyValues); if (existingEntity != null) { _logger.LogInformation($"[{typeof(T)}] 기존 데이터 발견: 기본 키 값({string.Join(", ", keyValues)})"); // 기존 엔티티를 업데이트: 새 entity의 값으로 교체 _dbContext.Entry(existingEntity).CurrentValues.SetValues(entity); } else { _logger.LogInformation($"[{typeof(T)}] 신규 데이터 등록: 기본 키 값({string.Join(", ", keyValues)})"); // 데이터가 없으면 새 엔티티 추가 _dbContext.Set().Add(entity); } } await _dbContext.SaveChangesAsync(); _logger.LogInformation($"[{typeof(T)}] DB 저장 완료: 종료"); return true; } catch (Exception ex) { _logger.LogInformation($"[{typeof(T)}] 저장 중 알 수 없는 오류 발생: {ex}"); return false; } } public async Task DeleteData(T entity, Expression> key = null) where T : class { try { if (key != null) { // key를 통해 조건식을 만들어 삭제할 엔티티를 찾는 경우 var value = key.Compile()(entity); var parameter = Expression.Parameter(typeof(T), "x"); var invokedExpr = Expression.Invoke(key, parameter); var constantExpr = Expression.Constant(value, key.Body.Type); var equalsExpr = Expression.Equal(invokedExpr, constantExpr); var predicate = Expression.Lambda>(equalsExpr, parameter); var entityData = await _dbContext.Set().FirstOrDefaultAsync(predicate); if (entityData == null) { _logger.LogInformation($"[{typeof(T)}] 삭제 대상 데이터가 존재하지 않습니다. (값 = {value})"); return false; } _logger.LogInformation($"[{typeof(T)}] 조건에 맞는 데이터 발견 (값 = {value}): 삭제 진행"); _dbContext.Set().Remove(entityData); } else { // key가 없는 경우 EF Core 메타데이터를 사용하여 기본 키를 통한 삭제 var entityType = _dbContext.Model.FindEntityType(typeof(T)); if (entityType == null) { throw new InvalidOperationException($"Entity type '{typeof(T).Name}'이 모델에 존재하지 않습니다."); } var primaryKey = entityType.FindPrimaryKey(); if (primaryKey == null) { throw new InvalidOperationException($"Entity type '{typeof(T).Name}'에 기본 키가 정의되어 있지 않습니다."); } var keyProperties = primaryKey.Properties; var keyValues = keyProperties.Select(p => typeof(T).GetProperty(p.Name).GetValue(entity)).ToArray(); var existingEntity = await _dbContext.Set().FindAsync(keyValues); if (existingEntity == null) { _logger.LogInformation($"[{typeof(T)}] 기본 키 값({string.Join(", ", keyValues)})에 해당하는 삭제 대상 데이터가 존재하지 않습니다."); return false; } _logger.LogInformation($"[{typeof(T)}] 기본 키 값({string.Join(", ", keyValues)})에 해당하는 데이터 발견: 삭제 진행"); _dbContext.Set().Remove(existingEntity); } await _dbContext.SaveChangesAsync(); _logger.LogInformation($"[{typeof(T)}] DB에서 삭제 완료"); return true; } catch (Exception ex) { _logger.LogError($"[{typeof(T)}] 삭제 중 오류 발생: {ex}"); return false; } } public string ReadSummary(Type type, string name) { var method = type.GetMethod(name) ?? throw new AcaException(ResposeCode.NetworkErr,"swagger summary Load ERROR: NULL"); var att = method.GetCustomAttribute() ?? throw new AcaException(ResposeCode.NetworkErr,"swagger summary Load ERROR: NULL"); return att.Summary; } public async Task SendFrontData(T data, string url) { using var httpClient = new HttpClient(); var json = JsonSerializer.Serialize(data); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await httpClient.PostAsync(url, content); response.EnsureSuccessStatusCode(); } } }