AcaMate_API/Program/V1/Services/RepositoryService.cs

194 lines
7.0 KiB
C#

using AcaMate.Common.Data;
using AcaMate.Common.Token;
using AcaMate.Common.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions;
using System.Security.Claims;
using AcaMate.V1.Models;
using Microsoft.IdentityModel.Tokens;
using Microsoft.VisualBasic;
namespace AcaMate.V1.Services;
public interface IRepositoryService
{
Task<bool> SaveData<T, K>(T entity, Expression<Func<T, K>> key) where T : class;
Task<ValidateToken> ValidateToken(string token, string refresh);
Task<bool> SaveDataFK<T>(T entity) where T : class;
}
public class RepositoryService: IRepositoryService
{
private readonly AppDbContext _dbContext;
private readonly ILogger<RepositoryService> _logger;
private readonly JwtTokenService _jwtTokenService;
public RepositoryService(AppDbContext dbContext, ILogger<RepositoryService> logger, JwtTokenService jwtTokenService)
{
_dbContext = dbContext;
_logger = logger;
_jwtTokenService = jwtTokenService;
}
public async Task<bool> SaveData<T, K> (T entity, Expression<Func<T, K>> key) where T : class
{
try
{
var value = key.Compile()(entity);
// x 라 함은 Expression 으로 생성되는 트리에서 T 타입으의 매개변수를 지칭함
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<Func<T, bool>>(equalsExpr, parameter);
var dbSet = _dbContext.Set<T>();
var entityData = await dbSet.FirstOrDefaultAsync(predicate);
if (entityData != null)
{
_logger.LogInformation($"[{typeof(T)}] 해당 PK 존재 [{value}]: 계속");
var entry = _dbContext.Entry(entityData);
entry.CurrentValues.SetValues(entity);
if (entry.Properties.Any(p => p.IsModified))
{
_logger.LogInformation($"[{typeof(T)}] 변경사항 존재: 계속");
}
else
{
_logger.LogInformation($"[{typeof(T)}] 변경사항 없음: 종료");
return true;
}
}
else
{
_logger.LogInformation($"[{typeof(T)}] 처음등록");
dbSet.Add(entity);
}
await _dbContext.SaveChangesAsync();
_logger.LogInformation($"[{typeof(T)}] DB 저장 완료: 종료");
return true;
}
catch (Exception ex)
{
_logger.LogInformation($"[{typeof(T)}] 알 수 없는 오류: 종료 {ex}");
return false;
}
}
//토큰 태울때는 인코딩 된 걸로 태워야지 원본꺼 태우면 데이터에 손상옵니다.
/// <summary>
/// 실제로 엑세스 토큰과 리프레시 토큰으로 접근 하기 위한 메서드
/// </summary>
public async Task<ValidateToken> ValidateToken(string token, string refresh)
{
var principalToken = _jwtTokenService.ValidateToken(token);
if (principalToken != null)
{
var uid = principalToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
_logger.LogInformation($"토큰 변환 - {uid}");
return new ValidateToken
{
token = token,
refresh = refresh,
uid = uid
};
}
else
{
_logger.LogInformation("엑세스 토큰 만료");
var refreshToken = await _dbContext.RefreshTokens
.FirstOrDefaultAsync(t => t.refresh_token == refresh);
if (refreshToken == null)
throw new TokenException("입력 받은 토큰 자체의 문제");
var uid = refreshToken.uid;
if (refreshToken.revoke_Date < DateTime.Now)
throw new RefreshRevokeException("리프레시 토큰 해지");
if (refreshToken.expire_date > DateTime.Now)
{
_logger.LogInformation($"인증 완료 리프레시 : {uid}");
var access = _jwtTokenService.GenerateJwtToken(uid);
return new ValidateToken
{
token = access,
refresh = refreshToken.refresh_token,
uid = uid
};
}
else
{
refreshToken = _jwtTokenService.GenerateRefreshToken(uid);
_logger.LogInformation("리프레시 토큰 만료");
await SaveData<RefreshToken, string>(refreshToken, rt => rt.uid);
return new ValidateToken
{
token = token,
refresh = refreshToken.refresh_token,
uid = uid
};
}
}
}
// public async Task SaveData<T, T1>(T pushCabinet)
// {
// throw new NotImplementedException();
// }
public async Task<bool> SaveDataFK<T>(T entity) where T : class
{
try
{
// EF Core 메타데이터를 통해 엔티티 T의 기본 키 속성 정보를 가져옴
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<T>().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<T>().Add(entity);
}
// 변경 사항 저장
await _dbContext.SaveChangesAsync();
_logger.LogInformation($"[{typeof(T)}] DB 저장 완료");
return true;
}
catch (Exception ex)
{
_logger.LogError($"[{typeof(T)}] 저장 중 오류 발생: {ex}");
return false;
}
}
}