diff --git a/SPMS.API/Controllers/ProfileController.cs b/SPMS.API/Controllers/ProfileController.cs new file mode 100644 index 0000000..b5c0795 --- /dev/null +++ b/SPMS.API/Controllers/ProfileController.cs @@ -0,0 +1,59 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Swashbuckle.AspNetCore.Annotations; +using SPMS.Application.DTOs.Account; +using SPMS.Application.Interfaces; +using SPMS.Domain.Common; + +namespace SPMS.API.Controllers; + +[ApiController] +[Route("v1/in/account/profile")] +[ApiExplorerSettings(GroupName = "account")] +[Authorize] +public class ProfileController : ControllerBase +{ + private readonly IAuthService _authService; + + public ProfileController(IAuthService authService) + { + _authService = authService; + } + + [HttpPost("info")] + [SwaggerOperation( + Summary = "내 정보 조회", + Description = "현재 로그인된 관리자의 프로필 정보를 조회합니다.")] + [SwaggerResponse(200, "조회 성공", typeof(ApiResponse))] + [SwaggerResponse(401, "인증되지 않은 요청")] + public async Task GetProfileAsync() + { + var adminIdClaim = User.FindFirst("adminId")?.Value; + if (string.IsNullOrEmpty(adminIdClaim) || !long.TryParse(adminIdClaim, out var adminId)) + { + return Unauthorized(ApiResponse.Fail("101", "인증 정보가 유효하지 않습니다.")); + } + + var result = await _authService.GetProfileAsync(adminId); + return Ok(ApiResponse.Success(result)); + } + + [HttpPost("update")] + [SwaggerOperation( + Summary = "내 정보 수정", + Description = "현재 로그인된 관리자의 프로필 정보(이름, 전화번호)를 수정합니다.")] + [SwaggerResponse(200, "수정 성공", typeof(ApiResponse))] + [SwaggerResponse(400, "변경된 내용 없음")] + [SwaggerResponse(401, "인증되지 않은 요청")] + public async Task UpdateProfileAsync([FromBody] UpdateProfileRequestDto request) + { + var adminIdClaim = User.FindFirst("adminId")?.Value; + if (string.IsNullOrEmpty(adminIdClaim) || !long.TryParse(adminIdClaim, out var adminId)) + { + return Unauthorized(ApiResponse.Fail("101", "인증 정보가 유효하지 않습니다.")); + } + + var result = await _authService.UpdateProfileAsync(adminId, request); + return Ok(ApiResponse.Success(result)); + } +} diff --git a/SPMS.Application/DTOs/Account/ProfileResponseDto.cs b/SPMS.Application/DTOs/Account/ProfileResponseDto.cs new file mode 100644 index 0000000..3077f2f --- /dev/null +++ b/SPMS.Application/DTOs/Account/ProfileResponseDto.cs @@ -0,0 +1,24 @@ +using System.Text.Json.Serialization; + +namespace SPMS.Application.DTOs.Account; + +public class ProfileResponseDto +{ + [JsonPropertyName("admin_code")] + public string AdminCode { get; set; } = string.Empty; + + [JsonPropertyName("email")] + public string Email { get; set; } = string.Empty; + + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + [JsonPropertyName("phone")] + public string Phone { get; set; } = string.Empty; + + [JsonPropertyName("role")] + public int Role { get; set; } + + [JsonPropertyName("created_at")] + public DateTime CreatedAt { get; set; } +} diff --git a/SPMS.Application/DTOs/Account/UpdateProfileRequestDto.cs b/SPMS.Application/DTOs/Account/UpdateProfileRequestDto.cs new file mode 100644 index 0000000..a7a0992 --- /dev/null +++ b/SPMS.Application/DTOs/Account/UpdateProfileRequestDto.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; + +namespace SPMS.Application.DTOs.Account; + +public class UpdateProfileRequestDto +{ + [StringLength(50, ErrorMessage = "이름은 50자 이내여야 합니다.")] + public string? Name { get; set; } + + [Phone(ErrorMessage = "올바른 전화번호 형식이 아닙니다.")] + [StringLength(20, ErrorMessage = "전화번호는 20자 이내여야 합니다.")] + public string? Phone { get; set; } +} diff --git a/SPMS.Application/Interfaces/IAuthService.cs b/SPMS.Application/Interfaces/IAuthService.cs index f082ef6..02b7201 100644 --- a/SPMS.Application/Interfaces/IAuthService.cs +++ b/SPMS.Application/Interfaces/IAuthService.cs @@ -1,3 +1,4 @@ +using SPMS.Application.DTOs.Account; using SPMS.Application.DTOs.Auth; namespace SPMS.Application.Interfaces; @@ -11,4 +12,6 @@ public interface IAuthService Task ChangePasswordAsync(long adminId, ChangePasswordRequestDto request); Task CheckEmailAsync(EmailCheckRequestDto request); Task VerifyEmailAsync(EmailVerifyRequestDto request); + Task GetProfileAsync(long adminId); + Task UpdateProfileAsync(long adminId, UpdateProfileRequestDto request); } diff --git a/SPMS.Application/Services/AuthService.cs b/SPMS.Application/Services/AuthService.cs index f500d6d..7ee4c33 100644 --- a/SPMS.Application/Services/AuthService.cs +++ b/SPMS.Application/Services/AuthService.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Options; +using SPMS.Application.DTOs.Account; using SPMS.Application.DTOs.Auth; using SPMS.Application.Interfaces; using SPMS.Application.Settings; @@ -275,4 +276,73 @@ public class AuthService : IAuthService await _tokenStore.RemoveAsync($"email_verify:{request.Email}"); } + + public async Task GetProfileAsync(long adminId) + { + var admin = await _adminRepository.GetByIdAsync(adminId); + if (admin is null) + { + throw new SpmsException( + ErrorCodes.NotFound, + "관리자를 찾을 수 없습니다.", + 404); + } + + return new ProfileResponseDto + { + AdminCode = admin.AdminCode, + Email = admin.Email, + Name = admin.Name, + Phone = admin.Phone, + Role = (int)admin.Role, + CreatedAt = admin.CreatedAt + }; + } + + public async Task UpdateProfileAsync(long adminId, UpdateProfileRequestDto request) + { + var admin = await _adminRepository.GetByIdAsync(adminId); + if (admin is null) + { + throw new SpmsException( + ErrorCodes.NotFound, + "관리자를 찾을 수 없습니다.", + 404); + } + + var hasChange = false; + + if (request.Name is not null && request.Name != admin.Name) + { + admin.Name = request.Name; + hasChange = true; + } + + if (request.Phone is not null && request.Phone != admin.Phone) + { + admin.Phone = request.Phone; + hasChange = true; + } + + if (!hasChange) + { + throw new SpmsException( + ErrorCodes.NoChange, + "변경된 내용이 없습니다.", + 400); + } + + _adminRepository.Update(admin); + await _unitOfWork.SaveChangesAsync(); + + return new ProfileResponseDto + { + AdminCode = admin.AdminCode, + Email = admin.Email, + Name = admin.Name, + Phone = admin.Phone, + Role = (int)admin.Role, + CreatedAt = admin.CreatedAt + }; + } }