diff --git a/.github/badges/branches.svg b/.github/badges/branches.svg index 2e69d9c..154eea8 100644 --- a/.github/badges/branches.svg +++ b/.github/badges/branches.svg @@ -1 +1 @@ -branches0% \ No newline at end of file +branches50% \ No newline at end of file diff --git a/.github/badges/jacoco.svg b/.github/badges/jacoco.svg index a944a8d..d6f2758 100644 --- a/.github/badges/jacoco.svg +++ b/.github/badges/jacoco.svg @@ -1 +1 @@ -coverage25.1% \ No newline at end of file +coverage44.6% \ No newline at end of file diff --git a/module-domain/src/main/java/com/gaethering/moduledomain/domain/member/Member.java b/module-domain/src/main/java/com/gaethering/moduledomain/domain/member/Member.java index 2a7020c..9e567fe 100644 --- a/module-domain/src/main/java/com/gaethering/moduledomain/domain/member/Member.java +++ b/module-domain/src/main/java/com/gaethering/moduledomain/domain/member/Member.java @@ -6,6 +6,7 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; +import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; @@ -51,7 +52,7 @@ public class Member extends BaseTimeEntity { @Enumerated(EnumType.STRING) private MemberRole role; - @OneToOne(fetch = FetchType.LAZY) + @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "member_profile_id") private MemberProfile memberProfile; diff --git a/module-member/build.gradle b/module-member/build.gradle index 7c9ca4f..d0be785 100644 --- a/module-member/build.gradle +++ b/module-member/build.gradle @@ -1,6 +1,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-mail' + implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-data-redis' runtimeOnly 'com.h2database:h2' diff --git a/module-member/src/main/java/com/gaethering/modulemember/controller/MemberController.java b/module-member/src/main/java/com/gaethering/modulemember/controller/MemberController.java index 04198f1..b22a2bc 100644 --- a/module-member/src/main/java/com/gaethering/modulemember/controller/MemberController.java +++ b/module-member/src/main/java/com/gaethering/modulemember/controller/MemberController.java @@ -3,6 +3,7 @@ import com.gaethering.modulemember.dto.ConfirmEmailRequest; import com.gaethering.modulemember.dto.ConfirmEmailResponse; import com.gaethering.modulemember.dto.EmailAuthRequest; +import com.gaethering.modulemember.dto.SignUpRequest; import com.gaethering.modulemember.service.MemberService; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @@ -19,6 +20,14 @@ public class MemberController { private final MemberService memberService; + @PostMapping("/sign-up") + public ResponseEntity signUp(@RequestBody SignUpRequest signUpRequest) { + + memberService.signUp(signUpRequest); + + return new ResponseEntity<>(HttpStatus.CREATED); + } + @PostMapping("/email-auth") public ResponseEntity sendEmailAuthCode(@RequestBody EmailAuthRequest emailAuthRequest) { memberService.sendEmailAuthCode(emailAuthRequest.getEmail()); diff --git a/module-member/src/main/java/com/gaethering/modulemember/dto/SignUpRequest.java b/module-member/src/main/java/com/gaethering/modulemember/dto/SignUpRequest.java new file mode 100644 index 0000000..b7f87c1 --- /dev/null +++ b/module-member/src/main/java/com/gaethering/modulemember/dto/SignUpRequest.java @@ -0,0 +1,36 @@ +package com.gaethering.modulemember.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gaethering.moduledomain.domain.type.Gender; +import java.time.LocalDate; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class SignUpRequest { + + private String email; + + private String nickname; + + private String password; + + private String passwordCheck; + + private String name; + + private String phone; + + private LocalDate birth; + + private Gender gender; + + @JsonProperty("isEmailAuth") + private boolean isEmailAuth; + +} diff --git a/module-member/src/main/java/com/gaethering/modulemember/exception/errorcode/MemberErrorCode.java b/module-member/src/main/java/com/gaethering/modulemember/exception/errorcode/MemberErrorCode.java index 028600f..8a7e598 100644 --- a/module-member/src/main/java/com/gaethering/modulemember/exception/errorcode/MemberErrorCode.java +++ b/module-member/src/main/java/com/gaethering/modulemember/exception/errorcode/MemberErrorCode.java @@ -9,7 +9,8 @@ public enum MemberErrorCode { MEMBER_NOT_FOUND("E001", "존재하지 않는 회원입니다."), INVALID_EMAIL_AUTH_CODE("E901", "이메일 인증 코드가 유효하지 않습니다."), DUPLICATED_EMAIL("E902", "중복된 이메일입니다."), - FAILED_SEND_EMAIL("E903", "이메일 전송에 실패하였습니다."); + FAILED_SEND_EMAIL("E903", "이메일 전송에 실패하였습니다."), + NOT_MATCH_PASSWORD("E904", "비밀번호가 서로 일치하지 않습니다."); private final String code; private final String message; diff --git a/module-member/src/main/java/com/gaethering/modulemember/exception/member/NotMatchPasswordException.java b/module-member/src/main/java/com/gaethering/modulemember/exception/member/NotMatchPasswordException.java new file mode 100644 index 0000000..2f8446a --- /dev/null +++ b/module-member/src/main/java/com/gaethering/modulemember/exception/member/NotMatchPasswordException.java @@ -0,0 +1,10 @@ +package com.gaethering.modulemember.exception.member; + +import com.gaethering.modulemember.exception.errorcode.MemberErrorCode; + +public class NotMatchPasswordException extends MemberException { + + public NotMatchPasswordException() { + super(MemberErrorCode.NOT_MATCH_PASSWORD); + } +} diff --git a/module-member/src/main/java/com/gaethering/modulemember/service/MemberService.java b/module-member/src/main/java/com/gaethering/modulemember/service/MemberService.java index 336112f..b8321c8 100644 --- a/module-member/src/main/java/com/gaethering/modulemember/service/MemberService.java +++ b/module-member/src/main/java/com/gaethering/modulemember/service/MemberService.java @@ -1,9 +1,13 @@ package com.gaethering.modulemember.service; +import com.gaethering.modulemember.dto.SignUpRequest; + public interface MemberService { void sendEmailAuthCode(String email); void confirmEmailAuthCode(String code); + String signUp(SignUpRequest signUpRequest); + } diff --git a/module-member/src/main/java/com/gaethering/modulemember/service/MemberServiceImpl.java b/module-member/src/main/java/com/gaethering/modulemember/service/MemberServiceImpl.java index c1ae8cd..99a16d6 100644 --- a/module-member/src/main/java/com/gaethering/modulemember/service/MemberServiceImpl.java +++ b/module-member/src/main/java/com/gaethering/modulemember/service/MemberServiceImpl.java @@ -1,10 +1,17 @@ package com.gaethering.modulemember.service; +import com.gaethering.moduledomain.domain.member.Member; +import com.gaethering.moduledomain.domain.member.MemberProfile; +import com.gaethering.moduledomain.domain.type.MemberRole; +import com.gaethering.moduledomain.domain.type.MemberStatus; import com.gaethering.moduledomain.repository.member.MemberRepository; +import com.gaethering.modulemember.dto.SignUpRequest; import com.gaethering.modulemember.exception.member.DuplicatedEmailException; +import com.gaethering.modulemember.exception.member.NotMatchPasswordException; import java.util.UUID; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -14,6 +21,7 @@ @Transactional(readOnly = true) public class MemberServiceImpl implements MemberService { + private final PasswordEncoder passwordEncoder; private final EmailService emailService; private final MemberRepository memberRepository; @@ -39,4 +47,34 @@ public void confirmEmailAuthCode(String code) { } + @Override + @Transactional + public String signUp(SignUpRequest signUpRequest) { + + if (memberRepository.existsByEmail(signUpRequest.getEmail())) { + throw new DuplicatedEmailException(); + } + + if (!signUpRequest.getPassword().equals(signUpRequest.getPasswordCheck())) { + throw new NotMatchPasswordException(); + } + + Member newMember = Member.builder() + .email(signUpRequest.getEmail()) + .nickname(signUpRequest.getNickname()) + .password(passwordEncoder.encode(signUpRequest.getPassword())) + .role(MemberRole.ROLE_USER) + .status(MemberStatus.ACTIVE) + .isEmailAuth(signUpRequest.isEmailAuth()) + .memberProfile(MemberProfile.builder() + .phoneNumber(signUpRequest.getPhone()) + .gender(signUpRequest.getGender()) + .build()) + .build(); + + memberRepository.save(newMember); + + return newMember.getNickname(); + } + } diff --git a/module-member/src/test/java/com/gaethering/modulemember/service/MemberServiceTest.java b/module-member/src/test/java/com/gaethering/modulemember/service/MemberServiceTest.java new file mode 100644 index 0000000..3f4a189 --- /dev/null +++ b/module-member/src/test/java/com/gaethering/modulemember/service/MemberServiceTest.java @@ -0,0 +1,131 @@ +package com.gaethering.modulemember.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import com.gaethering.moduledomain.domain.member.Member; +import com.gaethering.moduledomain.domain.type.Gender; +import com.gaethering.moduledomain.repository.member.MemberProfileRepository; +import com.gaethering.moduledomain.repository.member.MemberRepository; +import com.gaethering.modulemember.dto.SignUpRequest; +import com.gaethering.modulemember.exception.errorcode.MemberErrorCode; +import com.gaethering.modulemember.exception.member.DuplicatedEmailException; +import com.gaethering.modulemember.exception.member.NotMatchPasswordException; +import java.time.LocalDate; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.crypto.password.PasswordEncoder; + +@ExtendWith(MockitoExtension.class) +class MemberServiceTest { + + @Mock + private MemberRepository memberRepository; + + @Mock + private MemberProfileRepository memberProfileRepository; + + @Mock + private PasswordEncoder passwordEncoder; + + @InjectMocks + private MemberServiceImpl memberService; + + @Test + @DisplayName("회원 가입 성공") + void signUp_Success() { + //given + SignUpRequest request = SignUpRequest.builder() + .email("gaethering@gmail.com") + .nickname("개더링") + .password("1234qwer!") + .passwordCheck("1234qwer!") + .name("김진호") + .phone("010-3230-2498") + .birth(LocalDate.of(2022, 02, 15)) + .gender(Gender.MALE) + .isEmailAuth(true) + .build(); + + given(memberRepository.existsByEmail(anyString())) + .willReturn(false); + + given(passwordEncoder.encode(anyString())) + .willReturn(anyString()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Member.class); + + //when + String nickname = memberService.signUp(request); + + //then + assertEquals(request.getNickname(), nickname); + verify(memberRepository, times(1)).save(captor.capture()); + } + + @Test + @DisplayName("회원가입 실패_중복된 이메일이 있을 경우") + void signUp_ExceptionThrown_DuplicatedEmail() { + //given + SignUpRequest request = SignUpRequest.builder() + .email("gaethering@gmail.com") + .nickname("개더링") + .password("1234qwer!") + .passwordCheck("1234qwer!") + .name("김진호") + .phone("010-3230-2498") + .birth(LocalDate.of(2022, 02, 15)) + .gender(Gender.MALE) + .isEmailAuth(true) + .build(); + + given(memberRepository.existsByEmail(anyString())) + .willReturn(true); + + //when + DuplicatedEmailException exception = assertThrows( + DuplicatedEmailException.class, () -> memberService.signUp(request)); + + //then + assertEquals(MemberErrorCode.DUPLICATED_EMAIL.getCode(), + exception.getErrorCode().getCode()); + } + + @Test + @DisplayName("회원가입 실패_비밀번호가 일치하지 않는 경우") + void signUp_ExceptionThrown_NotMatchPassword() { + //given + SignUpRequest request = SignUpRequest.builder() + .email("gaethering@gmail.com") + .nickname("개더링") + .password("1234qwer!") + .passwordCheck("1234qwer") + .name("김진호") + .phone("010-3230-2498") + .birth(LocalDate.of(2022, 02, 15)) + .gender(Gender.MALE) + .isEmailAuth(true) + .build(); + + given(memberRepository.existsByEmail(anyString())) + .willReturn(false); + + //when + NotMatchPasswordException exception = assertThrows( + NotMatchPasswordException.class, () -> memberService.signUp(request)); + + //then + assertEquals(MemberErrorCode.NOT_MATCH_PASSWORD.getCode(), + exception.getErrorCode().getCode()); + } + +} \ No newline at end of file