diff --git a/src/main/java/com/softeer/backend/fo_domain/user/controller/LoginController.java b/src/main/java/com/softeer/backend/fo_domain/user/controller/LoginController.java new file mode 100644 index 00000000..28a394b6 --- /dev/null +++ b/src/main/java/com/softeer/backend/fo_domain/user/controller/LoginController.java @@ -0,0 +1,27 @@ +package com.softeer.backend.fo_domain.user.controller; + +import com.softeer.backend.fo_domain.user.dto.LoginRequest; +import com.softeer.backend.fo_domain.user.dto.UserTokenResponse; +import com.softeer.backend.fo_domain.user.service.LoginService; +import com.softeer.backend.global.common.code.status.SuccessStatus; +import com.softeer.backend.global.common.response.ResponseDto; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class LoginController { + + private final LoginService loginService; + + @PostMapping("/login") + ResponseDto handleLogin(@Valid @ModelAttribute LoginRequest loginRequest){ + UserTokenResponse userTokenResponse = loginService.handleLogin(loginRequest); + + return ResponseDto.onSuccess(SuccessStatus._LOGIN_SUCCESS, userTokenResponse); + } + +} diff --git a/src/main/java/com/softeer/backend/fo_domain/user/domain/User.java b/src/main/java/com/softeer/backend/fo_domain/user/domain/User.java index 748b7c5c..08190156 100644 --- a/src/main/java/com/softeer/backend/fo_domain/user/domain/User.java +++ b/src/main/java/com/softeer/backend/fo_domain/user/domain/User.java @@ -11,7 +11,10 @@ @AllArgsConstructor @Getter @Builder -@Table(name = "users") +@Table(name = "users", + indexes = { + @Index(name = "idx_users_phone_number", columnList = "phoneNumber") + }) public class User { @Id @Column(name = "user_id") @@ -24,6 +27,9 @@ public class User { @Column(name = "phone_number") private String phoneNumber; + @Column(name = "privacy_consent") + private boolean privacyConsent; + @Column(name = "marketing_consent") private boolean marketingConsent; } diff --git a/src/main/java/com/softeer/backend/fo_domain/user/dto/LoginRequest.java b/src/main/java/com/softeer/backend/fo_domain/user/dto/LoginRequest.java new file mode 100644 index 00000000..e6b08071 --- /dev/null +++ b/src/main/java/com/softeer/backend/fo_domain/user/dto/LoginRequest.java @@ -0,0 +1,27 @@ +package com.softeer.backend.fo_domain.user.dto; + +import com.softeer.backend.global.common.constant.ValidationConstant; +import jakarta.validation.constraints.Pattern; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder +@AllArgsConstructor +public class LoginRequest { + + private String name; + + @Pattern(regexp = ValidationConstant.PHONE_NUMBER_REGEX, + message = ValidationConstant.PHONE_NUMBER_MSG) + private String phoneNumber; + + private boolean isCodeVerified; + + private boolean privacyConsent; + + private boolean marketingConsent; +} diff --git a/src/main/java/com/softeer/backend/fo_domain/user/repository/UserRepository.java b/src/main/java/com/softeer/backend/fo_domain/user/repository/UserRepository.java index 041e2550..09ce0ec8 100644 --- a/src/main/java/com/softeer/backend/fo_domain/user/repository/UserRepository.java +++ b/src/main/java/com/softeer/backend/fo_domain/user/repository/UserRepository.java @@ -6,4 +6,7 @@ @Repository public interface UserRepository extends JpaRepository { + boolean existsByPhoneNumber(String phoneNumber); + + User findByPhoneNumber(String phoneNumber); } diff --git a/src/main/java/com/softeer/backend/fo_domain/user/service/LoginService.java b/src/main/java/com/softeer/backend/fo_domain/user/service/LoginService.java new file mode 100644 index 00000000..abde08e4 --- /dev/null +++ b/src/main/java/com/softeer/backend/fo_domain/user/service/LoginService.java @@ -0,0 +1,65 @@ +package com.softeer.backend.fo_domain.user.service; + +import com.softeer.backend.fo_domain.user.domain.User; +import com.softeer.backend.fo_domain.user.dto.LoginRequest; +import com.softeer.backend.fo_domain.user.dto.UserTokenResponse; +import com.softeer.backend.fo_domain.user.exception.UserException; +import com.softeer.backend.fo_domain.user.repository.UserRepository; +import com.softeer.backend.global.common.code.status.ErrorStatus; +import com.softeer.backend.global.common.constant.RoleType; +import com.softeer.backend.global.common.entity.JwtClaimsDto; +import com.softeer.backend.global.util.JwtUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class LoginService { + + private final UserRepository userRepository; + private final JwtUtil jwtUtil; + + /** + * 1. Login 정보애서 인증 번호가 인증되지 않은 경우, 예외가 발생한다. + * 2. 전화번호가 User DB에 등록되어 있지 않은 경우, DB에 User를 등록한다. + * 3. 전화번호가 이미 User DB에 등록되어 있는 경우, 전화번호로 User 객체를 조회한다. + * 4. User 객체의 id를 얻은 후에, access & refresh token을 client에게 전달한다. + */ + @Transactional + public UserTokenResponse handleLogin(LoginRequest loginRequest) { + + // 인증번호가 인증 되지 않은 경우, 예외 발생 + if(!loginRequest.isCodeVerified()) + throw new UserException(ErrorStatus._AUTH_CODE_NOT_VERIFIED); + + int userId; + + // 전화번호가 User DB에 등록되어 있지 않은 경우 + // User를 DB에 등록 + if(!userRepository.existsByPhoneNumber(loginRequest.getPhoneNumber())){ + User user = User.builder() + .name(loginRequest.getName()) + .phoneNumber(loginRequest.getPhoneNumber()) + .privacyConsent(loginRequest.isPrivacyConsent()) + .marketingConsent(loginRequest.isMarketingConsent()) + .build(); + + User registeredUser = userRepository.save(user); + userId = registeredUser.getId(); + } + // 전화번호가 이미 User DB에 등록되어 있는 경우 + // 전화번호로 User 객체 조회 + else{ + User user = userRepository.findByPhoneNumber(loginRequest.getPhoneNumber()); + userId = user.getId(); + } + + return jwtUtil.createServiceToken(JwtClaimsDto.builder() + .id(userId) + .roleType(RoleType.ROLE_USER) + .build()); + + } + +} diff --git a/src/main/java/com/softeer/backend/global/common/code/status/ErrorStatus.java b/src/main/java/com/softeer/backend/global/common/code/status/ErrorStatus.java index 04d3c0ee..cfdd84da 100644 --- a/src/main/java/com/softeer/backend/global/common/code/status/ErrorStatus.java +++ b/src/main/java/com/softeer/backend/global/common/code/status/ErrorStatus.java @@ -48,6 +48,7 @@ public enum ErrorStatus implements BaseErrorCode { "인증 코드의 인증 횟수를 초과하였습니다. 인증 코드 발급 API를 호출하세요."), _AUTH_CODE_ISSUE_LIMIT_EXCEEDED(HttpStatus.BAD_REQUEST, "AUTH_CODE_ISSUE_LIMIT_EXCEEDED", "인증 코드 발급 횟수를 초과하였습니다. 나중에 다시 시도하세요."), + _AUTH_CODE_NOT_VERIFIED(HttpStatus.BAD_REQUEST, "_AUTH_CODE_NOT_VERIFIED", "인증되지 않은 상태에서 로그인 할 수 없습니다."), // Share Error _SHARE_URL_NOT_FOUND(HttpStatus.NOT_FOUND, "SHARE_URL_NOT_FOUND", "공유 url이 없습니다."), diff --git a/src/main/java/com/softeer/backend/global/common/code/status/SuccessStatus.java b/src/main/java/com/softeer/backend/global/common/code/status/SuccessStatus.java index b0b6a639..81686442 100644 --- a/src/main/java/com/softeer/backend/global/common/code/status/SuccessStatus.java +++ b/src/main/java/com/softeer/backend/global/common/code/status/SuccessStatus.java @@ -21,7 +21,10 @@ public enum SuccessStatus implements BaseCode { // 전화번호 인증 _VERIFICATION_SEND(HttpStatus.OK, "VERIFICATION_SEND", "전화번호 인증 코드 전송 성공"), - _VERIFICATION_CONFIRM(HttpStatus.OK, "VERIFICATION_CONFIRM", "전화번호 인증 코드 검증 성공"); + _VERIFICATION_CONFIRM(HttpStatus.OK, "VERIFICATION_CONFIRM", "전화번호 인증 코드 검증 성공"), + + // 로그인 + _LOGIN_SUCCESS(HttpStatus.OK, "LOGIN_SUCCESS", "로그인 성공"); // 예외의 Http 상태값 private final HttpStatus httpStatus; diff --git a/src/test/java/com/softeer/backend/BackendApplicationTests.java b/src/test/java/com/softeer/backend/BackendApplicationTests.java index 777b1248..a5a1fc3a 100644 --- a/src/test/java/com/softeer/backend/BackendApplicationTests.java +++ b/src/test/java/com/softeer/backend/BackendApplicationTests.java @@ -9,5 +9,4 @@ class BackendApplicationTests { @Test void contextLoads() { } - }