Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[YS-256] feat: 회원가입 연락 받을 이메일 중복 비허용 로직 추가 #80

Merged
merged 11 commits into from
Feb 6, 2025
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class MemberService(
private val memberGateway: MemberGateway,
private val createParticipantUseCase: CreateParticipantUseCase,
private val createResearcherUseCase: CreateResearcherUseCase,
private val verifyContactEmailUseCase: VerifyContactEmailUseCase,
private val verifyResearcherEmailUseCase: VerifyResearcherEmailUseCase,
private val getResearcherInfoUseCase: GetResearcherInfoUseCase,
private val getParticipantInfoUseCase: GetParticipantInfoUseCase,
Expand All @@ -32,6 +33,11 @@ class MemberService(
return createResearcherUseCase.execute(input)
}

@Transactional
fun validateDuplicatedContactEmail(input: VerifyContactEmailUseCase.Input) : VerifyContactEmailUseCase.Output{
return verifyContactEmailUseCase.execute(input)
}

@Transactional
fun getResearcherInfo(input: GetResearcherInfoUseCase.Input): GetResearcherInfoUseCase.Output {
return getResearcherInfoUseCase.execute(input)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.dobby.backend.application.usecase.member

import com.dobby.backend.application.usecase.UseCase
import com.dobby.backend.domain.exception.ContactEmailDuplicateException
import com.dobby.backend.domain.gateway.member.MemberGateway

class VerifyContactEmailUseCase(
private val memberGateway: MemberGateway
): UseCase<VerifyContactEmailUseCase.Input, VerifyContactEmailUseCase.Output> {
data class Input(
val contactEmail: String
)

data class Output(
val success: Boolean
)

override fun execute(input: Input): Output {
if(!memberGateway.existsByContactEmail(input.contactEmail))
return Output(success = true)
else
throw ContactEmailDuplicateException
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ data object ResearcherNotFoundException : ClientException("ME0003", "Researcher
data object ParticipantNotFoundException : ClientException("ME0004", "Participant Not Found.", HttpStatus.NOT_FOUND)
data object EmailNotValidateException : ClientException("ME0005", "You should validate your school email first", HttpStatus.BAD_REQUEST)
data object SignupOauthEmailDuplicateException : ClientException("ME0006", "You've already joined with requested oauth email", HttpStatus.CONFLICT)

data object ContactEmailDuplicateException: ClientException("ME0007", "This contact email is already in use", HttpStatus.CONFLICT)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저번 PR에서랑 이번 PR에서 추가된 예외들은 노션에 있는 예외 코드 정리에 업데이트 해주시면 감사합니다~

/**
* Experiment error codes
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.dobby.backend.infrastructure.database.entity.enums.MemberStatus
interface MemberGateway {
fun getById(memberId: String): Member
fun findById(memberId: String): Member?
fun existsByContactEmail(contactEmail: String): Boolean
fun findByOauthEmailAndStatus(email: String, status: MemberStatus): Member?
fun findByOauthEmail(email: String): Member?
fun save(savedMember: Member) : Member
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class MemberEntity(
@Enumerated(EnumType.STRING)
val role: RoleType?,

@Column(name = "contact_email", length = 100, nullable = true)
@Column(name = "contact_email", length = 100, nullable = true, unique = true)
val contactEmail: String?,

@Column(name = "name", length = 10, nullable = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ import org.springframework.data.jpa.repository.JpaRepository

interface MemberRepository : JpaRepository<MemberEntity, String> {
fun findByOauthEmail(oauthEmail: String): MemberEntity?
fun existsByContactEmail(contactEmail: String): Boolean
fun findByOauthEmailAndStatus(oauthEmail: String, status: MemberStatus): MemberEntity?
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ class MemberGatewayImpl(
.orElse(null)
}

override fun existsByContactEmail(contactEmail: String): Boolean {
return memberRepository.existsByContactEmail(contactEmail)
}

override fun findByOauthEmailAndStatus(email: String, status: MemberStatus): Member? {
return memberRepository
.findByOauthEmailAndStatus(email, status)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package com.dobby.backend.presentation.api.controller

import com.dobby.backend.application.service.MemberService
import com.dobby.backend.presentation.api.dto.request.member.ParticipantSignupRequest
import com.dobby.backend.presentation.api.dto.request.member.ResearcherSignupRequest
import com.dobby.backend.presentation.api.dto.request.member.UpdateParticipantInfoRequest
import com.dobby.backend.presentation.api.dto.request.member.UpdateResearcherInfoRequest
import com.dobby.backend.presentation.api.dto.request.member.*
import com.dobby.backend.presentation.api.dto.response.member.DefaultResponse
import com.dobby.backend.presentation.api.dto.response.member.ParticipantInfoResponse
import com.dobby.backend.presentation.api.dto.response.member.ResearcherInfoResponse
import com.dobby.backend.presentation.api.dto.response.member.SignupResponse
Expand Down Expand Up @@ -50,6 +48,19 @@ class MemberController(
return MemberMapper.toResearcherSignupResponse(output)
}

@GetMapping("/signup/validate/contact-email")
@Operation(
summary = "연락 받을 이메일 주소 검증 API - 회원가입 시 필수 API",
description = "연락 받을 이메일이 사용 가능한지 검증해주는 API입니다. 사용가능하면 true, 아니면 예외를 던집니다."
)
fun emailAvailableCheck(
@RequestParam contactEmail: String
Comment on lines +56 to +57
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validateContactEmailForSignUp 이라는 함수명으로 변경하는 것은 어떨까요? 추후에 회원정보 수정을 위한 API도 추가해야 해서 함수명으로 구분이 가능하면 좋을 거 같아요!

) : DefaultResponse {
val input = MemberMapper.toContactEmailVerificationInput(contactEmail)
val output = memberService.validateDuplicatedContactEmail(input)
return MemberMapper.toContactEmailVerificationResponse(output)
}

@PreAuthorize("hasRole('RESEARCHER')")
@GetMapping("/researchers/me")
@Operation(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.dobby.backend.presentation.api.dto.request.member

import io.swagger.v3.oas.annotations.media.Schema
import jakarta.validation.constraints.Email
import jakarta.validation.constraints.NotBlank

data class ContactEmailVerificationRequest (
@Email(message= "연락 받을 이메일이 유효하지 않습니다.")
@NotBlank(message = "연락 받을 이메일은 공백일 수 없습니다.")
@Schema(description = "연락 받을 이메일")
val contactEmail: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ import com.dobby.backend.application.usecase.member.*
import com.dobby.backend.domain.model.member.Participant
import com.dobby.backend.infrastructure.database.entity.enums.areaInfo.Area
import com.dobby.backend.infrastructure.database.entity.enums.areaInfo.Region
import com.dobby.backend.presentation.api.dto.request.member.ParticipantSignupRequest
import com.dobby.backend.presentation.api.dto.request.member.ResearcherSignupRequest
import com.dobby.backend.presentation.api.dto.request.member.UpdateParticipantInfoRequest
import com.dobby.backend.presentation.api.dto.request.member.UpdateResearcherInfoRequest
import com.dobby.backend.presentation.api.dto.request.member.*
import com.dobby.backend.presentation.api.dto.response.member.*
import com.dobby.backend.util.getCurrentMemberId

Expand Down Expand Up @@ -61,6 +58,18 @@ object MemberMapper {
)
}

fun toContactEmailVerificationInput(contactEmail: String): VerifyContactEmailUseCase.Input{
return VerifyContactEmailUseCase.Input(
contactEmail = contactEmail
)
}

fun toContactEmailVerificationResponse(output: VerifyContactEmailUseCase.Output): DefaultResponse{
return DefaultResponse(
success = output.success
)
}

private fun toMemberResDto(input: Any): MemberResponse {
return when (input) {
is CreateResearcherUseCase.MemberResponse -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.dobby.backend.application.usecase.member

import com.dobby.backend.domain.exception.ContactEmailDuplicateException
import com.dobby.backend.domain.gateway.member.MemberGateway
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe
import io.kotest.assertions.throwables.shouldThrow
import io.mockk.every
import io.mockk.mockk

class VerifyContactEmailUseCaseTest : BehaviorSpec({

val memberGateway = mockk<MemberGateway>()
val verifyContactEmailUseCase = VerifyContactEmailUseCase(memberGateway)

given("회원 가입 시 이메일 중복 확인") {

`when`("이메일이 데이터베이스에 존재하지 않는 경우") {
val input = VerifyContactEmailUseCase.Input(contactEmail = "[email protected]")

every { memberGateway.existsByContactEmail(input.contactEmail) } returns false

then("이메일 사용 가능 응답을 반환해야 한다") {
val result = verifyContactEmailUseCase.execute(input)
result.success shouldBe true
}
}

`when`("이메일이 이미 존재하는 경우") {
val input = VerifyContactEmailUseCase.Input(contactEmail = "[email protected]")

every { memberGateway.existsByContactEmail(input.contactEmail) } returns true

then("ContactEmailDuplication 예외가 발생해야 한다") {
shouldThrow<ContactEmailDuplicateException> {
verifyContactEmailUseCase.execute(input)
}
}
}
}
})
Loading