using System.Linq.Expressions; using SPMS.Application.DTOs.Account; using SPMS.Application.Interfaces; using SPMS.Domain.Common; using SPMS.Domain.Entities; using SPMS.Domain.Enums; using SPMS.Domain.Exceptions; using SPMS.Domain.Interfaces; namespace SPMS.Application.Services; public class AccountService : IAccountService { private readonly IAdminRepository _adminRepository; private readonly IUnitOfWork _unitOfWork; public AccountService(IAdminRepository adminRepository, IUnitOfWork unitOfWork) { _adminRepository = adminRepository; _unitOfWork = unitOfWork; } public async Task CreateAsync(CreateAccountRequestDto request) { // 1. 이메일 중복 검사 if (await _adminRepository.EmailExistsAsync(request.Email)) { throw new SpmsException( ErrorCodes.Conflict, "이미 사용 중인 이메일입니다.", 409); } // 2. AdminCode 생성 (UUID) var adminCode = Guid.NewGuid().ToString("N")[..12].ToUpper(); // 3. Admin 엔티티 생성 var admin = new Admin { AdminCode = adminCode, Email = request.Email, Password = BCrypt.Net.BCrypt.HashPassword(request.Password), Name = request.Name, Phone = request.Phone ?? string.Empty, Role = (AdminRole)request.Role, EmailVerified = false, CreatedAt = DateTime.UtcNow, IsDeleted = false }; // 4. 저장 await _adminRepository.AddAsync(admin); await _unitOfWork.SaveChangesAsync(); // 5. 응답 반환 return MapToDto(admin); } public async Task GetListAsync(AccountListRequestDto request) { // 필터 조건 생성 (Super Admin 제외) Expression> predicate = a => a.Role != AdminRole.Super; // 검색어 필터 if (!string.IsNullOrWhiteSpace(request.SearchKeyword)) { var keyword = request.SearchKeyword; predicate = a => a.Role != AdminRole.Super && (a.Name.Contains(keyword) || a.Email.Contains(keyword)); } // Role 필터 if (request.Role.HasValue) { var role = (AdminRole)request.Role.Value; var basePredicate = predicate; predicate = a => basePredicate.Compile()(a) && a.Role == role; } // 페이징 조회 var (items, totalCount) = await _adminRepository.GetPagedAsync( request.Page, request.PageSize, predicate, a => a.CreatedAt, descending: true); return new AccountListResponseDto { Items = items.Select(MapToDto).ToList(), TotalCount = totalCount, Page = request.Page, PageSize = request.PageSize }; } public async Task GetByAdminCodeAsync(string adminCode) { var admin = await _adminRepository.GetByAdminCodeAsync(adminCode); if (admin is null) { throw new SpmsException( ErrorCodes.NotFound, "운영자를 찾을 수 없습니다.", 404); } // Super Admin 조회 불가 if (admin.Role == AdminRole.Super) { throw new SpmsException( ErrorCodes.NotFound, "운영자를 찾을 수 없습니다.", 404); } return MapToDto(admin); } public async Task UpdateAsync(string adminCode, UpdateAccountRequestDto request) { var admin = await _adminRepository.GetByAdminCodeAsync(adminCode); if (admin is null) { throw new SpmsException( ErrorCodes.NotFound, "운영자를 찾을 수 없습니다.", 404); } // Super Admin 수정 불가 if (admin.Role == AdminRole.Super) { throw new SpmsException( ErrorCodes.Forbidden, "Super Admin은 수정할 수 없습니다.", 403); } // 업데이트 admin.Name = request.Name; admin.Phone = request.Phone ?? string.Empty; admin.Role = (AdminRole)request.Role; _adminRepository.Update(admin); await _unitOfWork.SaveChangesAsync(); return MapToDto(admin); } public async Task DeleteAsync(string adminCode) { var admin = await _adminRepository.GetByAdminCodeAsync(adminCode); if (admin is null) { throw new SpmsException( ErrorCodes.NotFound, "운영자를 찾을 수 없습니다.", 404); } // Super Admin 삭제 불가 if (admin.Role == AdminRole.Super) { throw new SpmsException( ErrorCodes.Forbidden, "Super Admin은 삭제할 수 없습니다.", 403); } // Soft Delete admin.IsDeleted = true; admin.DeletedAt = DateTime.UtcNow; admin.RefreshToken = null; admin.RefreshTokenExpiresAt = null; _adminRepository.Update(admin); await _unitOfWork.SaveChangesAsync(); } private static AccountResponseDto MapToDto(Admin admin) { return new AccountResponseDto { AdminCode = admin.AdminCode, Email = admin.Email, Name = admin.Name, Phone = string.IsNullOrEmpty(admin.Phone) ? null : admin.Phone, Role = admin.Role.ToString(), EmailVerified = admin.EmailVerified, CreatedAt = admin.CreatedAt, LastLoginAt = admin.LastLoginAt }; } }