feat: 회원가입 API 구현 (#56)
- POST /v1/in/auth/signup 엔드포인트 추가 (AllowAnonymous) - SignupRequestDto/SignupResponseDto 생성 - AuthService.SignupAsync 구현 (이메일 중복검사, AdminCode 생성, BCrypt 해싱) - ApiResponse<T>.Success(data, msg) 오버로드 추가
This commit is contained in:
parent
b5b015255e
commit
16550dbff3
|
|
@ -19,6 +19,20 @@ public class AuthController : ControllerBase
|
|||
_authService = authService;
|
||||
}
|
||||
|
||||
[HttpPost("signup")]
|
||||
[AllowAnonymous]
|
||||
[SwaggerOperation(
|
||||
Summary = "회원가입",
|
||||
Description = "새로운 관리자 계정을 생성합니다. 이메일 인증이 필요합니다.")]
|
||||
[SwaggerResponse(200, "회원가입 성공", typeof(ApiResponse<SignupResponseDto>))]
|
||||
[SwaggerResponse(400, "잘못된 요청")]
|
||||
[SwaggerResponse(409, "이미 사용 중인 이메일")]
|
||||
public async Task<IActionResult> SignupAsync([FromBody] SignupRequestDto request)
|
||||
{
|
||||
var result = await _authService.SignupAsync(request);
|
||||
return Ok(ApiResponse<SignupResponseDto>.Success(result, "회원가입 완료. 이메일 인증이 필요합니다."));
|
||||
}
|
||||
|
||||
[HttpPost("login")]
|
||||
[AllowAnonymous]
|
||||
[SwaggerOperation(
|
||||
|
|
|
|||
22
SPMS.Application/DTOs/Auth/SignupRequestDto.cs
Normal file
22
SPMS.Application/DTOs/Auth/SignupRequestDto.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace SPMS.Application.DTOs.Auth;
|
||||
|
||||
public class SignupRequestDto
|
||||
{
|
||||
[Required(ErrorMessage = "이메일은 필수입니다.")]
|
||||
[EmailAddress(ErrorMessage = "올바른 이메일 형식이 아닙니다.")]
|
||||
public string Email { get; set; } = string.Empty;
|
||||
|
||||
[Required(ErrorMessage = "비밀번호는 필수입니다.")]
|
||||
[MinLength(8, ErrorMessage = "비밀번호는 8자 이상이어야 합니다.")]
|
||||
public string Password { get; set; } = string.Empty;
|
||||
|
||||
[Required(ErrorMessage = "이름은 필수입니다.")]
|
||||
[StringLength(50, ErrorMessage = "이름은 50자 이내여야 합니다.")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[Required(ErrorMessage = "전화번호는 필수입니다.")]
|
||||
[StringLength(20, ErrorMessage = "전화번호는 20자 이내여야 합니다.")]
|
||||
public string Phone { get; set; } = string.Empty;
|
||||
}
|
||||
12
SPMS.Application/DTOs/Auth/SignupResponseDto.cs
Normal file
12
SPMS.Application/DTOs/Auth/SignupResponseDto.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace SPMS.Application.DTOs.Auth;
|
||||
|
||||
public class SignupResponseDto
|
||||
{
|
||||
[JsonPropertyName("admin_code")]
|
||||
public string AdminCode { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("email")]
|
||||
public string Email { get; set; } = string.Empty;
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ namespace SPMS.Application.Interfaces;
|
|||
|
||||
public interface IAuthService
|
||||
{
|
||||
Task<SignupResponseDto> SignupAsync(SignupRequestDto request);
|
||||
Task<LoginResponseDto> LoginAsync(LoginRequestDto request);
|
||||
Task<TokenRefreshResponseDto> RefreshTokenAsync(TokenRefreshRequestDto request);
|
||||
Task LogoutAsync(long adminId);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ using SPMS.Application.DTOs.Auth;
|
|||
using SPMS.Application.Interfaces;
|
||||
using SPMS.Application.Settings;
|
||||
using SPMS.Domain.Common;
|
||||
using SPMS.Domain.Entities;
|
||||
using SPMS.Domain.Enums;
|
||||
using SPMS.Domain.Exceptions;
|
||||
using SPMS.Domain.Interfaces;
|
||||
|
||||
|
|
@ -27,6 +29,46 @@ public class AuthService : IAuthService
|
|||
_jwtSettings = jwtSettings.Value;
|
||||
}
|
||||
|
||||
public async Task<SignupResponseDto> SignupAsync(SignupRequestDto request)
|
||||
{
|
||||
// 1. 이메일 중복 검사
|
||||
if (await _adminRepository.EmailExistsAsync(request.Email))
|
||||
{
|
||||
throw new SpmsException(
|
||||
ErrorCodes.Conflict,
|
||||
"이미 사용 중인 이메일입니다.",
|
||||
409);
|
||||
}
|
||||
|
||||
// 2. AdminCode 생성 (UUID 12자)
|
||||
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,
|
||||
Role = AdminRole.User,
|
||||
EmailVerified = false,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
IsDeleted = false
|
||||
};
|
||||
|
||||
// 4. 저장
|
||||
await _adminRepository.AddAsync(admin);
|
||||
await _unitOfWork.SaveChangesAsync();
|
||||
|
||||
// 5. 응답 반환
|
||||
return new SignupResponseDto
|
||||
{
|
||||
AdminCode = admin.AdminCode,
|
||||
Email = admin.Email
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<LoginResponseDto> LoginAsync(LoginRequestDto request)
|
||||
{
|
||||
// 1. 이메일로 관리자 조회
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ public class ApiResponse<T> : ApiResponse
|
|||
public static ApiResponse<T> Success(T data)
|
||||
=> new() { Result = true, Code = ErrorCodes.Success, Data = data };
|
||||
|
||||
public static ApiResponse<T> Success(T data, string msg)
|
||||
=> new() { Result = true, Code = ErrorCodes.Success, Data = data, Msg = msg };
|
||||
|
||||
public new static ApiResponse<T> Fail(string code, string msg)
|
||||
=> new() { Result = false, Code = code, Msg = msg };
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user