From 73689fab2b383719e291af47ce92bc193a0ae1ab Mon Sep 17 00:00:00 2001 From: myqewr Date: Tue, 24 Jun 2025 17:40:11 +0900 Subject: [PATCH] =?UTF-8?q?refactor=20:=20=ED=9A=8C=EC=9B=90=20=EB=B3=B5?= =?UTF-8?q?=EA=B5=AC=EC=97=90=20=EB=94=B0=EB=A5=B8=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=EC=A0=88=EC=B0=A8=20=EC=88=98=EC=A0=95(#1?= =?UTF-8?q?36)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...deVerificationResponse_20250407231748.java | 13 ++ ...deVerificationResponse_20250622204707.java | 14 ++ ...deVerificationResponse_20250622204923.java | 14 ++ .../DeleteByEmailCommand_20250622213953.java | 1 + .../DeleteByEmailCommand_20250622213956.java | 12 + ...odeVerificationService_20250407231748.java | 35 +++ ...odeVerificationService_20250622204700.java | 43 ++++ ...odeVerificationService_20250622210129.java | 41 ++++ ...odeVerificationService_20250622210202.java | 43 ++++ ...uplicationCheckService_20250407231748.java | 20 ++ ...uplicationCheckService_20250622210504.java | 20 ++ ...neralUserSignupService_20250414190939.java | 67 ++++++ ...neralUserSignupService_20250622213609.java | 73 +++++++ ...neralUserSignupService_20250622213705.java | 73 +++++++ ...neralUserSignupService_20250622214029.java | 73 +++++++ ...neralUserSignupService_20250622214036.java | 74 +++++++ ...neralUserSignupService_20250622214043.java | 74 +++++++ ...neralUserSignupService_20250622214051.java | 74 +++++++ ...mailCodeVerificationVo_20250407231748.java | 12 + ...mailCodeVerificationVo_20250622204648.java | 17 ++ ...mailCodeVerificationVo_20250622204931.java | 17 ++ .../ftm/server/BaseTest_20250528010752.java | 196 +++++++++++++++++ .../ftm/server/BaseTest_20250622205652.java | 206 ++++++++++++++++++ .../ftm/server/BaseTest_20250622210207.java | 197 +++++++++++++++++ ...ilCodeVerificationTest_20250407231748.java | 89 ++++++++ ...ilCodeVerificationTest_20250622204717.java | 92 ++++++++ ...ilCodeVerificationTest_20250622205425.java | 92 ++++++++ ...ilCodeVerificationTest_20250622205721.java | 151 +++++++++++++ ...ilCodeVerificationTest_20250622210254.java | 160 ++++++++++++++ ...ilCodeVerificationTest_20250622211329.java | 134 ++++++++++++ ...ilCodeVerificationTest_20250622213651.java | 202 +++++++++++++++++ ...ilCodeVerificationTest_20250622213710.java | 205 +++++++++++++++++ .../EmailCodeVerificationResponse.java | 3 +- .../cache/LoadTrendingPostsTestAdapter.java | 87 ++++++++ .../user/UserDomainPersistenceAdapter.java | 22 ++ .../EmailVerificationLogsRepository.java | 7 + .../repository/UserRepository.java | 4 + .../command/user/DeleteByEmailCommand.java | 12 + .../user/DeleteUserByEmailCommand.java | 13 ++ .../in/user/UserHardDeleteByEmailUseCase.java | 10 + .../out/persistence/user/CheckUserPort.java | 4 + .../user/DeleteEmailVerificationLogPort.java | 9 + .../out/persistence/user/LoadUserPort.java | 3 + .../user/EmailCodeVerificationService.java | 10 +- .../user/EmailDuplicationCheckService.java | 2 +- .../user/GeneralUserSignupService.java | 19 +- .../user/UserHardDeleteByEmailService.java | 60 +++++ .../vo/user/EmailCodeVerificationVo.java | 7 +- src/test/java/com/ftm/server/BaseTest.java | 1 + .../user/EmailCodeVerificationTest.java | 32 ++- 50 files changed, 2833 insertions(+), 6 deletions(-) create mode 100644 .history/src/main/java/com/ftm/server/adapter/in/web/user/dto/response/EmailCodeVerificationResponse_20250407231748.java create mode 100644 .history/src/main/java/com/ftm/server/adapter/in/web/user/dto/response/EmailCodeVerificationResponse_20250622204707.java create mode 100644 .history/src/main/java/com/ftm/server/adapter/in/web/user/dto/response/EmailCodeVerificationResponse_20250622204923.java create mode 100644 .history/src/main/java/com/ftm/server/application/command/user/DeleteByEmailCommand_20250622213953.java create mode 100644 .history/src/main/java/com/ftm/server/application/command/user/DeleteByEmailCommand_20250622213956.java create mode 100644 .history/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService_20250407231748.java create mode 100644 .history/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService_20250622204700.java create mode 100644 .history/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService_20250622210129.java create mode 100644 .history/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService_20250622210202.java create mode 100644 .history/src/main/java/com/ftm/server/application/service/user/EmailDuplicationCheckService_20250407231748.java create mode 100644 .history/src/main/java/com/ftm/server/application/service/user/EmailDuplicationCheckService_20250622210504.java create mode 100644 .history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250414190939.java create mode 100644 .history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622213609.java create mode 100644 .history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622213705.java create mode 100644 .history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622214029.java create mode 100644 .history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622214036.java create mode 100644 .history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622214043.java create mode 100644 .history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622214051.java create mode 100644 .history/src/main/java/com/ftm/server/application/vo/user/EmailCodeVerificationVo_20250407231748.java create mode 100644 .history/src/main/java/com/ftm/server/application/vo/user/EmailCodeVerificationVo_20250622204648.java create mode 100644 .history/src/main/java/com/ftm/server/application/vo/user/EmailCodeVerificationVo_20250622204931.java create mode 100644 .history/src/test/java/com/ftm/server/BaseTest_20250528010752.java create mode 100644 .history/src/test/java/com/ftm/server/BaseTest_20250622205652.java create mode 100644 .history/src/test/java/com/ftm/server/BaseTest_20250622210207.java create mode 100644 .history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250407231748.java create mode 100644 .history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622204717.java create mode 100644 .history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622205425.java create mode 100644 .history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622205721.java create mode 100644 .history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622210254.java create mode 100644 .history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622211329.java create mode 100644 .history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622213651.java create mode 100644 .history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622213710.java create mode 100644 src/main/java/com/ftm/server/adapter/out/cache/LoadTrendingPostsTestAdapter.java create mode 100644 src/main/java/com/ftm/server/application/command/user/DeleteByEmailCommand.java create mode 100644 src/main/java/com/ftm/server/application/command/user/DeleteUserByEmailCommand.java create mode 100644 src/main/java/com/ftm/server/application/port/in/user/UserHardDeleteByEmailUseCase.java create mode 100644 src/main/java/com/ftm/server/application/port/out/persistence/user/DeleteEmailVerificationLogPort.java create mode 100644 src/main/java/com/ftm/server/application/service/user/UserHardDeleteByEmailService.java diff --git a/.history/src/main/java/com/ftm/server/adapter/in/web/user/dto/response/EmailCodeVerificationResponse_20250407231748.java b/.history/src/main/java/com/ftm/server/adapter/in/web/user/dto/response/EmailCodeVerificationResponse_20250407231748.java new file mode 100644 index 0000000..034acd6 --- /dev/null +++ b/.history/src/main/java/com/ftm/server/adapter/in/web/user/dto/response/EmailCodeVerificationResponse_20250407231748.java @@ -0,0 +1,13 @@ +package com.ftm.server.adapter.in.web.user.dto.response; + +import com.ftm.server.application.vo.user.EmailCodeVerificationVo; +import lombok.Data; + +@Data +public class EmailCodeVerificationResponse { + private final Boolean isVerified; + + public static EmailCodeVerificationResponse from(EmailCodeVerificationVo vo) { + return new EmailCodeVerificationResponse(vo.getIsVerified()); + } +} diff --git a/.history/src/main/java/com/ftm/server/adapter/in/web/user/dto/response/EmailCodeVerificationResponse_20250622204707.java b/.history/src/main/java/com/ftm/server/adapter/in/web/user/dto/response/EmailCodeVerificationResponse_20250622204707.java new file mode 100644 index 0000000..26fe9de --- /dev/null +++ b/.history/src/main/java/com/ftm/server/adapter/in/web/user/dto/response/EmailCodeVerificationResponse_20250622204707.java @@ -0,0 +1,14 @@ +package com.ftm.server.adapter.in.web.user.dto.response; + +import com.ftm.server.application.vo.user.EmailCodeVerificationVo; +import lombok.Data; + +@Data +public class EmailCodeVerificationResponse { + private final Boolean isVerified; + private final Boolean isRecoverable; + + public static EmailCodeVerificationResponse from(EmailCodeVerificationVo vo) { + return new EmailCodeVerificationResponse(vo.getIsVerified(), vo.getIsRecoverable()); + } +} diff --git a/.history/src/main/java/com/ftm/server/adapter/in/web/user/dto/response/EmailCodeVerificationResponse_20250622204923.java b/.history/src/main/java/com/ftm/server/adapter/in/web/user/dto/response/EmailCodeVerificationResponse_20250622204923.java new file mode 100644 index 0000000..26fe9de --- /dev/null +++ b/.history/src/main/java/com/ftm/server/adapter/in/web/user/dto/response/EmailCodeVerificationResponse_20250622204923.java @@ -0,0 +1,14 @@ +package com.ftm.server.adapter.in.web.user.dto.response; + +import com.ftm.server.application.vo.user.EmailCodeVerificationVo; +import lombok.Data; + +@Data +public class EmailCodeVerificationResponse { + private final Boolean isVerified; + private final Boolean isRecoverable; + + public static EmailCodeVerificationResponse from(EmailCodeVerificationVo vo) { + return new EmailCodeVerificationResponse(vo.getIsVerified(), vo.getIsRecoverable()); + } +} diff --git a/.history/src/main/java/com/ftm/server/application/command/user/DeleteByEmailCommand_20250622213953.java b/.history/src/main/java/com/ftm/server/application/command/user/DeleteByEmailCommand_20250622213953.java new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/.history/src/main/java/com/ftm/server/application/command/user/DeleteByEmailCommand_20250622213953.java @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/.history/src/main/java/com/ftm/server/application/command/user/DeleteByEmailCommand_20250622213956.java b/.history/src/main/java/com/ftm/server/application/command/user/DeleteByEmailCommand_20250622213956.java new file mode 100644 index 0000000..a1ddbe4 --- /dev/null +++ b/.history/src/main/java/com/ftm/server/application/command/user/DeleteByEmailCommand_20250622213956.java @@ -0,0 +1,12 @@ +package com.ftm.server.application.command.user; + +import lombok.Data; + +@Data +public class DeleteByEmailCommand { + private final String email; + + public static DeleteByEmailCommand of(String email) { + return new DeleteByEmailCommand(email); + } +} \ No newline at end of file diff --git a/.history/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService_20250407231748.java b/.history/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService_20250407231748.java new file mode 100644 index 0000000..ab0c6f9 --- /dev/null +++ b/.history/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService_20250407231748.java @@ -0,0 +1,35 @@ +package com.ftm.server.application.service.user; + +import com.ftm.server.application.port.in.user.EmailCodeVerificationUseCase; +import com.ftm.server.application.port.out.persistence.user.LoadEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.UpdateEmailVerificationLogPort; +import com.ftm.server.application.query.EmailCodeVerificationQuery; +import com.ftm.server.application.vo.user.EmailCodeVerificationVo; +import com.ftm.server.domain.entity.EmailVerificationLogs; +import jakarta.transaction.Transactional; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class EmailCodeVerificationService implements EmailCodeVerificationUseCase { + + private final LoadEmailVerificationLogPort loadEmailVerificationLogPort; + private final UpdateEmailVerificationLogPort updateEmailVerificationLogPort; + + @Override + @Transactional + public EmailCodeVerificationVo execute(EmailCodeVerificationQuery query) { + + Optional emailVerificationLogs = + loadEmailVerificationLogPort.loadEmailVerificationLogByEmailAndCode(query); + + if (emailVerificationLogs.isEmpty()) { // 검증 코드가 일치하지 않음 + return EmailCodeVerificationVo.of(false); + } + emailVerificationLogs.get().updateVerificationStatus(true); // 검증 코드가 일치함 + updateEmailVerificationLogPort.updateEmailVerificationLog(emailVerificationLogs.get()); + return EmailCodeVerificationVo.of(true); + } +} diff --git a/.history/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService_20250622204700.java b/.history/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService_20250622204700.java new file mode 100644 index 0000000..7aa9f56 --- /dev/null +++ b/.history/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService_20250622204700.java @@ -0,0 +1,43 @@ +package com.ftm.server.application.service.user; + +import com.ftm.server.application.port.in.user.EmailCodeVerificationUseCase; +import com.ftm.server.application.port.out.persistence.user.CheckUserPort; +import com.ftm.server.application.port.out.persistence.user.LoadEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.UpdateEmailVerificationLogPort; +import com.ftm.server.application.query.EmailCodeVerificationQuery; +import com.ftm.server.application.query.FindByEmailQuery; +import com.ftm.server.application.vo.user.EmailCodeVerificationVo; +import com.ftm.server.domain.entity.EmailVerificationLogs; +import jakarta.transaction.Transactional; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class EmailCodeVerificationService implements EmailCodeVerificationUseCase { + + private final LoadEmailVerificationLogPort loadEmailVerificationLogPort; + private final UpdateEmailVerificationLogPort updateEmailVerificationLogPort; + private final CheckUserPort checkUserPort; + + @Override + @Transactional + public EmailCodeVerificationVo execute(EmailCodeVerificationQuery query) { + + Optional emailVerificationLogs = + loadEmailVerificationLogPort.loadEmailVerificationLogByEmailAndCode(query); + + if (emailVerificationLogs.isEmpty()) { // 검증 코드가 일치하지 않음 + return EmailCodeVerificationVo.of(false); + } + + emailVerificationLogs.get().updateVerificationStatus(true); // 검증 코드가 일치함 + updateEmailVerificationLogPort.updateEmailVerificationLog(emailVerificationLogs.get()); + + // 해당 이메일의 계정 상태 확인 (soft delete 여부) + Boolean isRecoverable = !checkUserPort.checksNotDeletedUserByEmail(FindByEmailQuery.of(query.getEmail())); + + return EmailCodeVerificationVo.of(true, isRecoverable); + } +} diff --git a/.history/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService_20250622210129.java b/.history/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService_20250622210129.java new file mode 100644 index 0000000..6da83c7 --- /dev/null +++ b/.history/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService_20250622210129.java @@ -0,0 +1,41 @@ +package com.ftm.server.application.service.user; + +import com.ftm.server.application.port.in.user.EmailCodeVerificationUseCase; +import com.ftm.server.application.port.out.persistence.user.LoadEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.UpdateEmailVerificationLogPort; +import com.ftm.server.application.query.EmailCodeVerificationQuery; +import com.ftm.server.application.vo.user.EmailCodeVerificationVo; +import com.ftm.server.domain.entity.EmailVerificationLogs; +import jakarta.transaction.Transactional; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class EmailCodeVerificationService implements EmailCodeVerificationUseCase { + + private final LoadEmailVerificationLogPort loadEmailVerificationLogPort; + private final UpdateEmailVerificationLogPort updateEmailVerificationLogPort; + private final CheckUserPort checkUserPort; + + @Override + @Transactional + public EmailCodeVerificationVo execute(EmailCodeVerificationQuery query) { + + Optional emailVerificationLogs = + loadEmailVerificationLogPort.loadEmailVerificationLogByEmailAndCode(query); + + if (emailVerificationLogs.isEmpty()) { // 검증 코드가 일치하지 않음 + return EmailCodeVerificationVo.of(false); + } + + emailVerificationLogs.get().updateVerificationStatus(true); // 검증 코드가 일치함 + updateEmailVerificationLogPort.updateEmailVerificationLog(emailVerificationLogs.get()); + + // 해당 이메일의 계정 상태 확인 (soft delete 여부) + Boolean isRecoverable = checkUserPort.checksUserSoftDeletedByEmail(FindByEmailQuery.of(query.getEmail())); + + return EmailCodeVerificationVo.of(true, isRecoverable); + } +} diff --git a/.history/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService_20250622210202.java b/.history/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService_20250622210202.java new file mode 100644 index 0000000..464378e --- /dev/null +++ b/.history/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService_20250622210202.java @@ -0,0 +1,43 @@ +package com.ftm.server.application.service.user; + +import com.ftm.server.application.port.in.user.EmailCodeVerificationUseCase; +import com.ftm.server.application.port.out.persistence.user.CheckUserPort; +import com.ftm.server.application.port.out.persistence.user.LoadEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.UpdateEmailVerificationLogPort; +import com.ftm.server.application.query.EmailCodeVerificationQuery; +import com.ftm.server.application.query.FindByEmailQuery; +import com.ftm.server.application.vo.user.EmailCodeVerificationVo; +import com.ftm.server.domain.entity.EmailVerificationLogs; +import jakarta.transaction.Transactional; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class EmailCodeVerificationService implements EmailCodeVerificationUseCase { + + private final LoadEmailVerificationLogPort loadEmailVerificationLogPort; + private final UpdateEmailVerificationLogPort updateEmailVerificationLogPort; + private final CheckUserPort checkUserPort; + + @Override + @Transactional + public EmailCodeVerificationVo execute(EmailCodeVerificationQuery query) { + + Optional emailVerificationLogs = + loadEmailVerificationLogPort.loadEmailVerificationLogByEmailAndCode(query); + + if (emailVerificationLogs.isEmpty()) { // 검증 코드가 일치하지 않음 + return EmailCodeVerificationVo.of(false); + } + + emailVerificationLogs.get().updateVerificationStatus(true); // 검증 코드가 일치함 + updateEmailVerificationLogPort.updateEmailVerificationLog(emailVerificationLogs.get()); + + // 해당 이메일의 계정 상태 확인 (soft delete 여부) + Boolean isRecoverable = checkUserPort.checksUserSoftDeletedByEmail(FindByEmailQuery.of(query.getEmail())); + + return EmailCodeVerificationVo.of(true, isRecoverable); + } +} diff --git a/.history/src/main/java/com/ftm/server/application/service/user/EmailDuplicationCheckService_20250407231748.java b/.history/src/main/java/com/ftm/server/application/service/user/EmailDuplicationCheckService_20250407231748.java new file mode 100644 index 0000000..ba96dc9 --- /dev/null +++ b/.history/src/main/java/com/ftm/server/application/service/user/EmailDuplicationCheckService_20250407231748.java @@ -0,0 +1,20 @@ +package com.ftm.server.application.service.user; + +import com.ftm.server.application.port.in.user.EmailDuplicationCheckUseCase; +import com.ftm.server.application.port.out.persistence.user.CheckUserPort; +import com.ftm.server.application.query.FindByEmailQuery; +import com.ftm.server.application.vo.user.EmailDuplicationVo; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class EmailDuplicationCheckService implements EmailDuplicationCheckUseCase { + + private final CheckUserPort checkUserPort; + + @Override + public EmailDuplicationVo execute(FindByEmailQuery query) { + return EmailDuplicationVo.of(checkUserPort.checksUserByEmail(query)); + } +} diff --git a/.history/src/main/java/com/ftm/server/application/service/user/EmailDuplicationCheckService_20250622210504.java b/.history/src/main/java/com/ftm/server/application/service/user/EmailDuplicationCheckService_20250622210504.java new file mode 100644 index 0000000..d1b7fd5 --- /dev/null +++ b/.history/src/main/java/com/ftm/server/application/service/user/EmailDuplicationCheckService_20250622210504.java @@ -0,0 +1,20 @@ +package com.ftm.server.application.service.user; + +import com.ftm.server.application.port.in.user.EmailDuplicationCheckUseCase; +import com.ftm.server.application.port.out.persistence.user.CheckUserPort; +import com.ftm.server.application.query.FindByEmailQuery; +import com.ftm.server.application.vo.user.EmailDuplicationVo; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class EmailDuplicationCheckService implements EmailDuplicationCheckUseCase { + + private final CheckUserPort checkUserPort; + + @Override + public EmailDuplicationVo execute(FindByEmailQuery query) { + return EmailDuplicationVo.of(checkUserPort.checksNotDeletedUserByEmail(query)); + } +} diff --git a/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250414190939.java b/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250414190939.java new file mode 100644 index 0000000..a3f77ef --- /dev/null +++ b/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250414190939.java @@ -0,0 +1,67 @@ +package com.ftm.server.application.service.user; + +import com.ftm.server.adapter.in.web.user.dto.response.GeneralUserSignupResponse; +import com.ftm.server.application.command.user.GeneralUserCreationCommand; +import com.ftm.server.application.command.user.GeneralUserSignupCommand; +import com.ftm.server.application.port.in.user.GeneralUserSignupUseCase; +import com.ftm.server.application.port.out.persistence.user.CheckUserPort; +import com.ftm.server.application.port.out.persistence.user.LoadEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.SaveUserImagePort; +import com.ftm.server.application.port.out.persistence.user.SaveUserPort; +import com.ftm.server.application.port.out.security.SecurityAuthenticationPort; +import com.ftm.server.application.query.FindByEmailQuery; +import com.ftm.server.common.exception.CustomException; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.common.utils.RandomNickNameCreator; +import com.ftm.server.domain.entity.EmailVerificationLogs; +import com.ftm.server.domain.entity.User; +import com.ftm.server.domain.entity.UserImage; +import jakarta.transaction.Transactional; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class GeneralUserSignupService implements GeneralUserSignupUseCase { + + // service + private final LoadEmailVerificationLogPort loadEmailVerificationLogPort; + private final CheckUserPort checksUserPort; + private final SaveUserPort saveUserPort; + private final SaveUserImagePort saveUserImagePort; + + // gateway + private final SecurityAuthenticationPort authenticationPort; + + @Transactional + @Override + public GeneralUserSignupResponse execute(GeneralUserSignupCommand command) { + String email = command.getEmail(); + Optional emailVerificationLogs = + loadEmailVerificationLogPort.loadEmailVerificationLogByEmail( + FindByEmailQuery.of(email)); + + if (checksUserPort.checksUserByEmail(FindByEmailQuery.of(email))) { // 기존에 가입된 회원인지 검사 + throw new CustomException(ErrorResponseCode.USER_ALREADY_EXISTS); + } + + if (emailVerificationLogs.isEmpty()) { // 이메일 인증이 완료되지 않음. + throw new CustomException(ErrorResponseCode.EMAIL_NOT_VERIFIED); + } + + String nickname = RandomNickNameCreator.generateNickname(); // random 닉네임 생성 + + GeneralUserCreationCommand convertedCommand = + GeneralUserCreationCommand.of( + command.getEmail(), + authenticationPort.passwordEncode(command.getPassword()), + nickname, + command.getAge(), + command.getHashtags()); + + User user = saveUserPort.saveUser(User.createGeneralUser(convertedCommand)); + saveUserImagePort.saveUserDefaultImage(UserImage.createUserImage(user.getId())); + return GeneralUserSignupResponse.of(user.getId()); + } +} diff --git a/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622213609.java b/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622213609.java new file mode 100644 index 0000000..19fc9c5 --- /dev/null +++ b/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622213609.java @@ -0,0 +1,73 @@ +package com.ftm.server.application.service.user; + +import com.ftm.server.adapter.in.web.user.dto.response.GeneralUserSignupResponse; +import com.ftm.server.application.command.user.GeneralUserCreationCommand; +import com.ftm.server.application.command.user.GeneralUserSignupCommand; +import com.ftm.server.application.port.in.user.GeneralUserSignupUseCase; +import com.ftm.server.application.port.out.persistence.user.CheckUserPort; +import com.ftm.server.application.port.out.persistence.user.DeleteEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.LoadEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.SaveUserImagePort; +import com.ftm.server.application.port.out.persistence.user.SaveUserPort; +import com.ftm.server.application.port.out.security.SecurityAuthenticationPort; +import com.ftm.server.application.query.FindByEmailQuery; +import com.ftm.server.common.exception.CustomException; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.common.utils.RandomNickNameCreator; +import com.ftm.server.domain.entity.EmailVerificationLogs; +import com.ftm.server.domain.entity.User; +import com.ftm.server.domain.entity.UserImage; +import jakarta.transaction.Transactional; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class GeneralUserSignupService implements GeneralUserSignupUseCase { + + // service + private final LoadEmailVerificationLogPort loadEmailVerificationLogPort; + private final DeleteEmailVerificationLogPort deleteEmailVerificationLogPort; + private final CheckUserPort checksUserPort; + private final SaveUserPort saveUserPort; + private final SaveUserImagePort saveUserImagePort; + + // gateway + private final SecurityAuthenticationPort authenticationPort; + + @Transactional + @Override + public GeneralUserSignupResponse execute(GeneralUserSignupCommand command) { + String email = command.getEmail(); + Optional emailVerificationLogs = + loadEmailVerificationLogPort.loadEmailVerificationLogByEmail( + FindByEmailQuery.of(email)); + + if (checksUserPort.checksUserByEmail(FindByEmailQuery.of(email))) { // 기존에 가입된 회원인지 검사 + throw new CustomException(ErrorResponseCode.USER_ALREADY_EXISTS); + } + + if (emailVerificationLogs.isEmpty()) { // 이메일 인증이 완료되지 않음. + throw new CustomException(ErrorResponseCode.EMAIL_NOT_VERIFIED); + } + + String nickname = RandomNickNameCreator.generateNickname(); // random 닉네임 생성 + + GeneralUserCreationCommand convertedCommand = + GeneralUserCreationCommand.of( + command.getEmail(), + authenticationPort.passwordEncode(command.getPassword()), + nickname, + command.getAge(), + command.getHashtags()); + + User user = saveUserPort.saveUser(User.createGeneralUser(convertedCommand)); + saveUserImagePort.saveUserDefaultImage(UserImage.createUserImage(user.getId())); + + // 회원가입 완료 후 해당 이메일의 인증 로그 삭제 + deleteEmailVerificationLogPort.deleteEmailVerificationLogsByEmail(FindByEmailQuery.of(email)); + + return GeneralUserSignupResponse.of(user.getId()); + } +} diff --git a/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622213705.java b/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622213705.java new file mode 100644 index 0000000..19fc9c5 --- /dev/null +++ b/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622213705.java @@ -0,0 +1,73 @@ +package com.ftm.server.application.service.user; + +import com.ftm.server.adapter.in.web.user.dto.response.GeneralUserSignupResponse; +import com.ftm.server.application.command.user.GeneralUserCreationCommand; +import com.ftm.server.application.command.user.GeneralUserSignupCommand; +import com.ftm.server.application.port.in.user.GeneralUserSignupUseCase; +import com.ftm.server.application.port.out.persistence.user.CheckUserPort; +import com.ftm.server.application.port.out.persistence.user.DeleteEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.LoadEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.SaveUserImagePort; +import com.ftm.server.application.port.out.persistence.user.SaveUserPort; +import com.ftm.server.application.port.out.security.SecurityAuthenticationPort; +import com.ftm.server.application.query.FindByEmailQuery; +import com.ftm.server.common.exception.CustomException; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.common.utils.RandomNickNameCreator; +import com.ftm.server.domain.entity.EmailVerificationLogs; +import com.ftm.server.domain.entity.User; +import com.ftm.server.domain.entity.UserImage; +import jakarta.transaction.Transactional; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class GeneralUserSignupService implements GeneralUserSignupUseCase { + + // service + private final LoadEmailVerificationLogPort loadEmailVerificationLogPort; + private final DeleteEmailVerificationLogPort deleteEmailVerificationLogPort; + private final CheckUserPort checksUserPort; + private final SaveUserPort saveUserPort; + private final SaveUserImagePort saveUserImagePort; + + // gateway + private final SecurityAuthenticationPort authenticationPort; + + @Transactional + @Override + public GeneralUserSignupResponse execute(GeneralUserSignupCommand command) { + String email = command.getEmail(); + Optional emailVerificationLogs = + loadEmailVerificationLogPort.loadEmailVerificationLogByEmail( + FindByEmailQuery.of(email)); + + if (checksUserPort.checksUserByEmail(FindByEmailQuery.of(email))) { // 기존에 가입된 회원인지 검사 + throw new CustomException(ErrorResponseCode.USER_ALREADY_EXISTS); + } + + if (emailVerificationLogs.isEmpty()) { // 이메일 인증이 완료되지 않음. + throw new CustomException(ErrorResponseCode.EMAIL_NOT_VERIFIED); + } + + String nickname = RandomNickNameCreator.generateNickname(); // random 닉네임 생성 + + GeneralUserCreationCommand convertedCommand = + GeneralUserCreationCommand.of( + command.getEmail(), + authenticationPort.passwordEncode(command.getPassword()), + nickname, + command.getAge(), + command.getHashtags()); + + User user = saveUserPort.saveUser(User.createGeneralUser(convertedCommand)); + saveUserImagePort.saveUserDefaultImage(UserImage.createUserImage(user.getId())); + + // 회원가입 완료 후 해당 이메일의 인증 로그 삭제 + deleteEmailVerificationLogPort.deleteEmailVerificationLogsByEmail(FindByEmailQuery.of(email)); + + return GeneralUserSignupResponse.of(user.getId()); + } +} diff --git a/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622214029.java b/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622214029.java new file mode 100644 index 0000000..19fc9c5 --- /dev/null +++ b/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622214029.java @@ -0,0 +1,73 @@ +package com.ftm.server.application.service.user; + +import com.ftm.server.adapter.in.web.user.dto.response.GeneralUserSignupResponse; +import com.ftm.server.application.command.user.GeneralUserCreationCommand; +import com.ftm.server.application.command.user.GeneralUserSignupCommand; +import com.ftm.server.application.port.in.user.GeneralUserSignupUseCase; +import com.ftm.server.application.port.out.persistence.user.CheckUserPort; +import com.ftm.server.application.port.out.persistence.user.DeleteEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.LoadEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.SaveUserImagePort; +import com.ftm.server.application.port.out.persistence.user.SaveUserPort; +import com.ftm.server.application.port.out.security.SecurityAuthenticationPort; +import com.ftm.server.application.query.FindByEmailQuery; +import com.ftm.server.common.exception.CustomException; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.common.utils.RandomNickNameCreator; +import com.ftm.server.domain.entity.EmailVerificationLogs; +import com.ftm.server.domain.entity.User; +import com.ftm.server.domain.entity.UserImage; +import jakarta.transaction.Transactional; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class GeneralUserSignupService implements GeneralUserSignupUseCase { + + // service + private final LoadEmailVerificationLogPort loadEmailVerificationLogPort; + private final DeleteEmailVerificationLogPort deleteEmailVerificationLogPort; + private final CheckUserPort checksUserPort; + private final SaveUserPort saveUserPort; + private final SaveUserImagePort saveUserImagePort; + + // gateway + private final SecurityAuthenticationPort authenticationPort; + + @Transactional + @Override + public GeneralUserSignupResponse execute(GeneralUserSignupCommand command) { + String email = command.getEmail(); + Optional emailVerificationLogs = + loadEmailVerificationLogPort.loadEmailVerificationLogByEmail( + FindByEmailQuery.of(email)); + + if (checksUserPort.checksUserByEmail(FindByEmailQuery.of(email))) { // 기존에 가입된 회원인지 검사 + throw new CustomException(ErrorResponseCode.USER_ALREADY_EXISTS); + } + + if (emailVerificationLogs.isEmpty()) { // 이메일 인증이 완료되지 않음. + throw new CustomException(ErrorResponseCode.EMAIL_NOT_VERIFIED); + } + + String nickname = RandomNickNameCreator.generateNickname(); // random 닉네임 생성 + + GeneralUserCreationCommand convertedCommand = + GeneralUserCreationCommand.of( + command.getEmail(), + authenticationPort.passwordEncode(command.getPassword()), + nickname, + command.getAge(), + command.getHashtags()); + + User user = saveUserPort.saveUser(User.createGeneralUser(convertedCommand)); + saveUserImagePort.saveUserDefaultImage(UserImage.createUserImage(user.getId())); + + // 회원가입 완료 후 해당 이메일의 인증 로그 삭제 + deleteEmailVerificationLogPort.deleteEmailVerificationLogsByEmail(FindByEmailQuery.of(email)); + + return GeneralUserSignupResponse.of(user.getId()); + } +} diff --git a/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622214036.java b/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622214036.java new file mode 100644 index 0000000..a49ae03 --- /dev/null +++ b/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622214036.java @@ -0,0 +1,74 @@ +package com.ftm.server.application.service.user; + +import com.ftm.server.adapter.in.web.user.dto.response.GeneralUserSignupResponse; +import com.ftm.server.application.command.user.DeleteByEmailCommand; +import com.ftm.server.application.command.user.GeneralUserCreationCommand; +import com.ftm.server.application.command.user.GeneralUserSignupCommand; +import com.ftm.server.application.port.in.user.GeneralUserSignupUseCase; +import com.ftm.server.application.port.out.persistence.user.CheckUserPort; +import com.ftm.server.application.port.out.persistence.user.DeleteEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.LoadEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.SaveUserImagePort; +import com.ftm.server.application.port.out.persistence.user.SaveUserPort; +import com.ftm.server.application.port.out.security.SecurityAuthenticationPort; +import com.ftm.server.application.query.FindByEmailQuery; +import com.ftm.server.common.exception.CustomException; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.common.utils.RandomNickNameCreator; +import com.ftm.server.domain.entity.EmailVerificationLogs; +import com.ftm.server.domain.entity.User; +import com.ftm.server.domain.entity.UserImage; +import jakarta.transaction.Transactional; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class GeneralUserSignupService implements GeneralUserSignupUseCase { + + // service + private final LoadEmailVerificationLogPort loadEmailVerificationLogPort; + private final DeleteEmailVerificationLogPort deleteEmailVerificationLogPort; + private final CheckUserPort checksUserPort; + private final SaveUserPort saveUserPort; + private final SaveUserImagePort saveUserImagePort; + + // gateway + private final SecurityAuthenticationPort authenticationPort; + + @Transactional + @Override + public GeneralUserSignupResponse execute(GeneralUserSignupCommand command) { + String email = command.getEmail(); + Optional emailVerificationLogs = + loadEmailVerificationLogPort.loadEmailVerificationLogByEmail( + FindByEmailQuery.of(email)); + + if (checksUserPort.checksUserByEmail(FindByEmailQuery.of(email))) { // 기존에 가입된 회원인지 검사 + throw new CustomException(ErrorResponseCode.USER_ALREADY_EXISTS); + } + + if (emailVerificationLogs.isEmpty()) { // 이메일 인증이 완료되지 않음. + throw new CustomException(ErrorResponseCode.EMAIL_NOT_VERIFIED); + } + + String nickname = RandomNickNameCreator.generateNickname(); // random 닉네임 생성 + + GeneralUserCreationCommand convertedCommand = + GeneralUserCreationCommand.of( + command.getEmail(), + authenticationPort.passwordEncode(command.getPassword()), + nickname, + command.getAge(), + command.getHashtags()); + + User user = saveUserPort.saveUser(User.createGeneralUser(convertedCommand)); + saveUserImagePort.saveUserDefaultImage(UserImage.createUserImage(user.getId())); + + // 회원가입 완료 후 해당 이메일의 인증 로그 삭제 + deleteEmailVerificationLogPort.deleteEmailVerificationLogsByEmail(FindByEmailQuery.of(email)); + + return GeneralUserSignupResponse.of(user.getId()); + } +} diff --git a/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622214043.java b/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622214043.java new file mode 100644 index 0000000..5edb0a1 --- /dev/null +++ b/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622214043.java @@ -0,0 +1,74 @@ +package com.ftm.server.application.service.user; + +import com.ftm.server.adapter.in.web.user.dto.response.GeneralUserSignupResponse; +import com.ftm.server.application.command.user.DeleteByEmailCommand; +import com.ftm.server.application.command.user.GeneralUserCreationCommand; +import com.ftm.server.application.command.user.GeneralUserSignupCommand; +import com.ftm.server.application.port.in.user.GeneralUserSignupUseCase; +import com.ftm.server.application.port.out.persistence.user.CheckUserPort; +import com.ftm.server.application.port.out.persistence.user.DeleteEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.LoadEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.SaveUserImagePort; +import com.ftm.server.application.port.out.persistence.user.SaveUserPort; +import com.ftm.server.application.port.out.security.SecurityAuthenticationPort; +import com.ftm.server.application.query.FindByEmailQuery; +import com.ftm.server.common.exception.CustomException; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.common.utils.RandomNickNameCreator; +import com.ftm.server.domain.entity.EmailVerificationLogs; +import com.ftm.server.domain.entity.User; +import com.ftm.server.domain.entity.UserImage; +import jakarta.transaction.Transactional; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class GeneralUserSignupService implements GeneralUserSignupUseCase { + + // service + private final LoadEmailVerificationLogPort loadEmailVerificationLogPort; + private final DeleteEmailVerificationLogPort deleteEmailVerificationLogPort; + private final CheckUserPort checksUserPort; + private final SaveUserPort saveUserPort; + private final SaveUserImagePort saveUserImagePort; + + // gateway + private final SecurityAuthenticationPort authenticationPort; + + @Transactional + @Override + public GeneralUserSignupResponse execute(GeneralUserSignupCommand command) { + String email = command.getEmail(); + Optional emailVerificationLogs = + loadEmailVerificationLogPort.loadEmailVerificationLogByEmail( + FindByEmailQuery.of(email)); + + if (checksUserPort.checksUserByEmail(FindByEmailQuery.of(email))) { // 기존에 가입된 회원인지 검사 + throw new CustomException(ErrorResponseCode.USER_ALREADY_EXISTS); + } + + if (emailVerificationLogs.isEmpty()) { // 이메일 인증이 완료되지 않음. + throw new CustomException(ErrorResponseCode.EMAIL_NOT_VERIFIED); + } + + String nickname = RandomNickNameCreator.generateNickname(); // random 닉네임 생성 + + GeneralUserCreationCommand convertedCommand = + GeneralUserCreationCommand.of( + command.getEmail(), + authenticationPort.passwordEncode(command.getPassword()), + nickname, + command.getAge(), + command.getHashtags()); + + User user = saveUserPort.saveUser(User.createGeneralUser(convertedCommand)); + saveUserImagePort.saveUserDefaultImage(UserImage.createUserImage(user.getId())); + + // 회원가입 완료 후 해당 이메일의 인증 로그 삭제 + deleteEmailVerificationLogPort.deleteEmailVerificationLogsByEmail(DeleteByEmailCommand.of(email)); + + return GeneralUserSignupResponse.of(user.getId()); + } +} diff --git a/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622214051.java b/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622214051.java new file mode 100644 index 0000000..5edb0a1 --- /dev/null +++ b/.history/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService_20250622214051.java @@ -0,0 +1,74 @@ +package com.ftm.server.application.service.user; + +import com.ftm.server.adapter.in.web.user.dto.response.GeneralUserSignupResponse; +import com.ftm.server.application.command.user.DeleteByEmailCommand; +import com.ftm.server.application.command.user.GeneralUserCreationCommand; +import com.ftm.server.application.command.user.GeneralUserSignupCommand; +import com.ftm.server.application.port.in.user.GeneralUserSignupUseCase; +import com.ftm.server.application.port.out.persistence.user.CheckUserPort; +import com.ftm.server.application.port.out.persistence.user.DeleteEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.LoadEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.SaveUserImagePort; +import com.ftm.server.application.port.out.persistence.user.SaveUserPort; +import com.ftm.server.application.port.out.security.SecurityAuthenticationPort; +import com.ftm.server.application.query.FindByEmailQuery; +import com.ftm.server.common.exception.CustomException; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.common.utils.RandomNickNameCreator; +import com.ftm.server.domain.entity.EmailVerificationLogs; +import com.ftm.server.domain.entity.User; +import com.ftm.server.domain.entity.UserImage; +import jakarta.transaction.Transactional; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class GeneralUserSignupService implements GeneralUserSignupUseCase { + + // service + private final LoadEmailVerificationLogPort loadEmailVerificationLogPort; + private final DeleteEmailVerificationLogPort deleteEmailVerificationLogPort; + private final CheckUserPort checksUserPort; + private final SaveUserPort saveUserPort; + private final SaveUserImagePort saveUserImagePort; + + // gateway + private final SecurityAuthenticationPort authenticationPort; + + @Transactional + @Override + public GeneralUserSignupResponse execute(GeneralUserSignupCommand command) { + String email = command.getEmail(); + Optional emailVerificationLogs = + loadEmailVerificationLogPort.loadEmailVerificationLogByEmail( + FindByEmailQuery.of(email)); + + if (checksUserPort.checksUserByEmail(FindByEmailQuery.of(email))) { // 기존에 가입된 회원인지 검사 + throw new CustomException(ErrorResponseCode.USER_ALREADY_EXISTS); + } + + if (emailVerificationLogs.isEmpty()) { // 이메일 인증이 완료되지 않음. + throw new CustomException(ErrorResponseCode.EMAIL_NOT_VERIFIED); + } + + String nickname = RandomNickNameCreator.generateNickname(); // random 닉네임 생성 + + GeneralUserCreationCommand convertedCommand = + GeneralUserCreationCommand.of( + command.getEmail(), + authenticationPort.passwordEncode(command.getPassword()), + nickname, + command.getAge(), + command.getHashtags()); + + User user = saveUserPort.saveUser(User.createGeneralUser(convertedCommand)); + saveUserImagePort.saveUserDefaultImage(UserImage.createUserImage(user.getId())); + + // 회원가입 완료 후 해당 이메일의 인증 로그 삭제 + deleteEmailVerificationLogPort.deleteEmailVerificationLogsByEmail(DeleteByEmailCommand.of(email)); + + return GeneralUserSignupResponse.of(user.getId()); + } +} diff --git a/.history/src/main/java/com/ftm/server/application/vo/user/EmailCodeVerificationVo_20250407231748.java b/.history/src/main/java/com/ftm/server/application/vo/user/EmailCodeVerificationVo_20250407231748.java new file mode 100644 index 0000000..022e946 --- /dev/null +++ b/.history/src/main/java/com/ftm/server/application/vo/user/EmailCodeVerificationVo_20250407231748.java @@ -0,0 +1,12 @@ +package com.ftm.server.application.vo.user; + +import lombok.Data; + +@Data +public class EmailCodeVerificationVo { + private final Boolean isVerified; + + public static EmailCodeVerificationVo of(Boolean isVerified) { + return new EmailCodeVerificationVo(isVerified); + } +} diff --git a/.history/src/main/java/com/ftm/server/application/vo/user/EmailCodeVerificationVo_20250622204648.java b/.history/src/main/java/com/ftm/server/application/vo/user/EmailCodeVerificationVo_20250622204648.java new file mode 100644 index 0000000..d7988fd --- /dev/null +++ b/.history/src/main/java/com/ftm/server/application/vo/user/EmailCodeVerificationVo_20250622204648.java @@ -0,0 +1,17 @@ +package com.ftm.server.application.vo.user; + +import lombok.Data; + +@Data +public class EmailCodeVerificationVo { + private final Boolean isVerified; + private final Boolean isRecoverable; // 계정 복구 가능 여부 (soft delete 상태인지) + + public static EmailCodeVerificationVo of(Boolean isVerified) { + return new EmailCodeVerificationVo(isVerified, false); + } + + public static EmailCodeVerificationVo of(Boolean isVerified, Boolean isRecoverable) { + return new EmailCodeVerificationVo(isVerified, isRecoverable); + } +} diff --git a/.history/src/main/java/com/ftm/server/application/vo/user/EmailCodeVerificationVo_20250622204931.java b/.history/src/main/java/com/ftm/server/application/vo/user/EmailCodeVerificationVo_20250622204931.java new file mode 100644 index 0000000..d7988fd --- /dev/null +++ b/.history/src/main/java/com/ftm/server/application/vo/user/EmailCodeVerificationVo_20250622204931.java @@ -0,0 +1,17 @@ +package com.ftm.server.application.vo.user; + +import lombok.Data; + +@Data +public class EmailCodeVerificationVo { + private final Boolean isVerified; + private final Boolean isRecoverable; // 계정 복구 가능 여부 (soft delete 상태인지) + + public static EmailCodeVerificationVo of(Boolean isVerified) { + return new EmailCodeVerificationVo(isVerified, false); + } + + public static EmailCodeVerificationVo of(Boolean isVerified, Boolean isRecoverable) { + return new EmailCodeVerificationVo(isVerified, isRecoverable); + } +} diff --git a/.history/src/test/java/com/ftm/server/BaseTest_20250528010752.java b/.history/src/test/java/com/ftm/server/BaseTest_20250528010752.java new file mode 100644 index 0000000..6e4bfc4 --- /dev/null +++ b/.history/src/test/java/com/ftm/server/BaseTest_20250528010752.java @@ -0,0 +1,196 @@ +package com.ftm.server; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyHeaders; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ftm.server.application.command.user.GeneralUserCreationCommand; +import com.ftm.server.application.port.out.persistence.auth.LoadUserForAuthPort; +import com.ftm.server.application.port.out.persistence.user.SaveUserImagePort; +import com.ftm.server.application.port.out.persistence.user.SaveUserPort; +import com.ftm.server.application.query.FindByEmailQuery; +import com.ftm.server.domain.entity.User; +import com.ftm.server.domain.entity.UserImage; +import com.ftm.server.domain.enums.AgeGroup; +import com.ftm.server.domain.enums.HashTag; +import com.ftm.server.domain.enums.UserRole; +import com.ftm.server.infrastructure.security.UserPrincipal; +import groovy.util.logging.Slf4j; +import java.util.List; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; +import org.springframework.restdocs.operation.preprocess.HeadersModifyingOperationPreprocessor; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers; +import org.springframework.security.web.context.HttpSessionSecurityContextRepository; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +@Slf4j +@AutoConfigureMockMvc +@AutoConfigureRestDocs +@SpringBootTest(classes = {ServerApplication.class}) +@ActiveProfiles("test") +@ExtendWith({RestDocumentationExtension.class}) +@AutoConfigureTestDatabase( + replace = AutoConfigureTestDatabase.Replace.NONE) // 테스트 시 내장된 인메모리 DB를 사용하지 않는다는 설정 +@TestPropertySource(locations = "file:.env") +public class BaseTest { + + @Autowired protected MockMvc mockMvc; + + protected final ObjectMapper mapper = new ObjectMapper(); + + @Autowired private SaveUserPort saveUserPort; + @Autowired private SaveUserImagePort saveUserImagePort; + @Autowired private LoadUserForAuthPort loadUserForAuthPort; + + @BeforeEach + void setup(WebApplicationContext context, RestDocumentationContextProvider restDocumentation) { + this.mockMvc = + MockMvcBuilders.webAppContextSetup(context) + .apply(SecurityMockMvcConfigurers.springSecurity()) // secrutiy filter 적용 + .apply(documentationConfiguration(restDocumentation)) + .build(); + } + + // 블필요한 header 제거 함수 + protected HeadersModifyingOperationPreprocessor getModifiedHeader() { + return modifyHeaders() + .remove("X-Content-Type-Options") + .remove("X-XSS-Protection") + .remove("Cache-Control") + .remove("Pragma") + .remove("Expires") + .remove("Content-Length") + .remove("X-Frame-Options") + .remove("Vary"); + } + + // 사용자 생성 및 저장 + protected User createTestUser(String email, String password) { + String nickname = "test " + UUID.randomUUID(); + User user = + User.createGeneralUser( + GeneralUserCreationCommand.of( + email, + password, + nickname, + AgeGroup.FIFTIES, + List.of(HashTag.PERFUME))); + User testUser = saveUserPort.saveUser(user); + saveUserImagePort.saveUserDefaultImage(UserImage.createUserImage(testUser.getId())); + return testUser; + } + + protected MockHttpSession createUserAndLogin() { + return createUserAndLogin("test@gmail.com", "123456qwe!"); + } + + // test 사용자 생성 후 mock session 생성 + protected MockHttpSession createUserAndLogin(String email, String password) { + + // 사용자 생성 + User user = createTestUser(email, password); + + // session 생성 + SecurityContext context = SecurityContextHolder.createEmptyContext(); + UsernamePasswordAuthenticationToken auth = + new UsernamePasswordAuthenticationToken( + UserPrincipal.of(user), + null, + List.of(new SimpleGrantedAuthority(UserRole.USER.name()))); + context.setAuthentication(auth); + + MockHttpSession session = new MockHttpSession(); + session.setAttribute( + HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context); + + return session; + } + + protected MockHttpSession login(String email) { + User user = loadUserForAuthPort.loadUserByEmail(FindByEmailQuery.of(email)).get(); + + // session 생성 + SecurityContext context = SecurityContextHolder.createEmptyContext(); + UsernamePasswordAuthenticationToken auth = + new UsernamePasswordAuthenticationToken( + UserPrincipal.of(user), + null, + List.of(new SimpleGrantedAuthority(UserRole.USER.name()))); + context.setAuthentication(auth); + + MockHttpSession session = new MockHttpSession(); + session.setAttribute( + HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context); + + return session; + } + + public record SessionAndUser(MockHttpSession mockHttpSession, User user) {} + + // Session과 함께 User도 반환 + protected SessionAndUser createUserAndLoginAndReturnUser() { + return createUserAndLoginAndReturnUser("test@gmail.com", "123456qwe!"); + } + + protected SessionAndUser createUserAndLoginAndReturnUser(String email, String password) { + + // 사용자 생성 + User user = createTestUser(email, password); + + // session 생성 + SecurityContext context = SecurityContextHolder.createEmptyContext(); + UsernamePasswordAuthenticationToken auth = + new UsernamePasswordAuthenticationToken( + UserPrincipal.of(user), + null, + List.of(new SimpleGrantedAuthority(UserRole.USER.name()))); + context.setAuthentication(auth); + + MockHttpSession session = new MockHttpSession(); + session.setAttribute( + HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context); + + return new SessionAndUser(session, user); + } + + protected MockHttpSession createAdminUserAndLogin() { + String email = "admin@gmail.com"; + String password = "admin1234!"; + String nickname = "admintest"; + + User admin = User.createAdminUser(email, password, nickname); + + // session 생성 + SecurityContext context = SecurityContextHolder.createEmptyContext(); + UsernamePasswordAuthenticationToken auth = + new UsernamePasswordAuthenticationToken( + UserPrincipal.of(admin), + null, + List.of(new SimpleGrantedAuthority("ROLE_" + UserRole.ADMIN.name()))); + context.setAuthentication(auth); + + MockHttpSession session = new MockHttpSession(); + session.setAttribute( + HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context); + + return session; + } +} diff --git a/.history/src/test/java/com/ftm/server/BaseTest_20250622205652.java b/.history/src/test/java/com/ftm/server/BaseTest_20250622205652.java new file mode 100644 index 0000000..3943899 --- /dev/null +++ b/.history/src/test/java/com/ftm/server/BaseTest_20250622205652.java @@ -0,0 +1,206 @@ +package com.ftm.server; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyHeaders; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ftm.server.application.command.user.GeneralUserCreationCommand; +import com.ftm.server.application.port.out.persistence.auth.LoadUserForAuthPort; +import com.ftm.server.application.port.out.persistence.user.SaveUserImagePort; +import com.ftm.server.application.port.out.persistence.user.SaveUserPort; +import com.ftm.server.application.query.FindByEmailQuery; +import com.ftm.server.domain.entity.User; +import com.ftm.server.domain.entity.UserImage; +import com.ftm.server.domain.enums.AgeGroup; +import com.ftm.server.domain.enums.HashTag; +import com.ftm.server.domain.enums.UserRole; +import com.ftm.server.infrastructure.security.UserPrincipal; +import groovy.util.logging.Slf4j; +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; +import org.springframework.restdocs.operation.preprocess.HeadersModifyingOperationPreprocessor; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers; +import org.springframework.security.web.context.HttpSessionSecurityContextRepository; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +@Slf4j +@AutoConfigureMockMvc +@AutoConfigureRestDocs +@SpringBootTest(classes = {ServerApplication.class}) +@ActiveProfiles("test") +@ExtendWith({RestDocumentationExtension.class}) +@AutoConfigureTestDatabase( + replace = AutoConfigureTestDatabase.Replace.NONE) // 테스트 시 내장된 인메모리 DB를 사용하지 않는다는 설정 +@TestPropertySource(locations = "file:.env") +public class BaseTest { + + @Autowired protected MockMvc mockMvc; + + protected final ObjectMapper mapper = new ObjectMapper(); + + @Autowired private SaveUserPort saveUserPort; + @Autowired private SaveUserImagePort saveUserImagePort; + @Autowired private LoadUserForAuthPort loadUserForAuthPort; + + @BeforeEach + void setup(WebApplicationContext context, RestDocumentationContextProvider restDocumentation) { + this.mockMvc = + MockMvcBuilders.webAppContextSetup(context) + .apply(SecurityMockMvcConfigurers.springSecurity()) // secrutiy filter 적용 + .apply(documentationConfiguration(restDocumentation)) + .build(); + } + + // 블필요한 header 제거 함수 + protected HeadersModifyingOperationPreprocessor getModifiedHeader() { + return modifyHeaders() + .remove("X-Content-Type-Options") + .remove("X-XSS-Protection") + .remove("Cache-Control") + .remove("Pragma") + .remove("Expires") + .remove("Content-Length") + .remove("X-Frame-Options") + .remove("Vary"); + } + + // 사용자 생성 및 저장 + protected User createTestUser(String email, String password) { + String nickname = "test " + UUID.randomUUID(); + User user = + User.createGeneralUser( + GeneralUserCreationCommand.of( + email, + password, + nickname, + AgeGroup.FIFTIES, + List.of(HashTag.PERFUME))); + User testUser = saveUserPort.saveUser(user); + saveUserImagePort.saveUserDefaultImage(UserImage.createUserImage(testUser.getId())); + return testUser; + } + + // 사용자를 soft delete 상태로 만드는 헬퍼 메서드 + protected User createSoftDeletedUser(String email, String password) { + User user = createTestUser(email, password); + user.updateIsDeleted(true); + user.updateDeletedAt(LocalDateTime.now()); + saveUserPort.saveUser(user); + return user; + } + + protected MockHttpSession createUserAndLogin() { + return createUserAndLogin("test@gmail.com", "123456qwe!"); + } + + // test 사용자 생성 후 mock session 생성 + protected MockHttpSession createUserAndLogin(String email, String password) { + + // 사용자 생성 + User user = createTestUser(email, password); + + // session 생성 + SecurityContext context = SecurityContextHolder.createEmptyContext(); + UsernamePasswordAuthenticationToken auth = + new UsernamePasswordAuthenticationToken( + UserPrincipal.of(user), + null, + List.of(new SimpleGrantedAuthority(UserRole.USER.name()))); + context.setAuthentication(auth); + + MockHttpSession session = new MockHttpSession(); + session.setAttribute( + HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context); + + return session; + } + + protected MockHttpSession login(String email) { + User user = loadUserForAuthPort.loadUserByEmail(FindByEmailQuery.of(email)).get(); + + // session 생성 + SecurityContext context = SecurityContextHolder.createEmptyContext(); + UsernamePasswordAuthenticationToken auth = + new UsernamePasswordAuthenticationToken( + UserPrincipal.of(user), + null, + List.of(new SimpleGrantedAuthority(UserRole.USER.name()))); + context.setAuthentication(auth); + + MockHttpSession session = new MockHttpSession(); + session.setAttribute( + HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context); + + return session; + } + + public record SessionAndUser(MockHttpSession mockHttpSession, User user) {} + + // Session과 함께 User도 반환 + protected SessionAndUser createUserAndLoginAndReturnUser() { + return createUserAndLoginAndReturnUser("test@gmail.com", "123456qwe!"); + } + + protected SessionAndUser createUserAndLoginAndReturnUser(String email, String password) { + + // 사용자 생성 + User user = createTestUser(email, password); + + // session 생성 + SecurityContext context = SecurityContextHolder.createEmptyContext(); + UsernamePasswordAuthenticationToken auth = + new UsernamePasswordAuthenticationToken( + UserPrincipal.of(user), + null, + List.of(new SimpleGrantedAuthority(UserRole.USER.name()))); + context.setAuthentication(auth); + + MockHttpSession session = new MockHttpSession(); + session.setAttribute( + HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context); + + return new SessionAndUser(session, user); + } + + protected MockHttpSession createAdminUserAndLogin() { + String email = "admin@gmail.com"; + String password = "admin1234!"; + String nickname = "admintest"; + + User admin = User.createAdminUser(email, password, nickname); + + // session 생성 + SecurityContext context = SecurityContextHolder.createEmptyContext(); + UsernamePasswordAuthenticationToken auth = + new UsernamePasswordAuthenticationToken( + UserPrincipal.of(admin), + null, + List.of(new SimpleGrantedAuthority("ROLE_" + UserRole.ADMIN.name()))); + context.setAuthentication(auth); + + MockHttpSession session = new MockHttpSession(); + session.setAttribute( + HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context); + + return session; + } +} diff --git a/.history/src/test/java/com/ftm/server/BaseTest_20250622210207.java b/.history/src/test/java/com/ftm/server/BaseTest_20250622210207.java new file mode 100644 index 0000000..72b2ff2 --- /dev/null +++ b/.history/src/test/java/com/ftm/server/BaseTest_20250622210207.java @@ -0,0 +1,197 @@ +package com.ftm.server; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyHeaders; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ftm.server.application.command.user.GeneralUserCreationCommand; +import com.ftm.server.application.port.out.persistence.auth.LoadUserForAuthPort; +import com.ftm.server.application.port.out.persistence.user.SaveUserImagePort; +import com.ftm.server.application.port.out.persistence.user.SaveUserPort; +import com.ftm.server.application.query.FindByEmailQuery; +import com.ftm.server.domain.entity.User; +import com.ftm.server.domain.entity.UserImage; +import com.ftm.server.domain.enums.AgeGroup; +import com.ftm.server.domain.enums.HashTag; +import com.ftm.server.domain.enums.UserRole; +import com.ftm.server.infrastructure.security.UserPrincipal; +import groovy.util.logging.Slf4j; +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; +import org.springframework.restdocs.operation.preprocess.HeadersModifyingOperationPreprocessor; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers; +import org.springframework.security.web.context.HttpSessionSecurityContextRepository; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +@Slf4j +@AutoConfigureMockMvc +@AutoConfigureRestDocs +@SpringBootTest(classes = {ServerApplication.class}) +@ActiveProfiles("test") +@ExtendWith({RestDocumentationExtension.class}) +@AutoConfigureTestDatabase( + replace = AutoConfigureTestDatabase.Replace.NONE) // 테스트 시 내장된 인메모리 DB를 사용하지 않는다는 설정 +@TestPropertySource(locations = "file:.env") +public class BaseTest { + + @Autowired protected MockMvc mockMvc; + + protected final ObjectMapper mapper = new ObjectMapper(); + + @Autowired private SaveUserPort saveUserPort; + @Autowired private SaveUserImagePort saveUserImagePort; + @Autowired private LoadUserForAuthPort loadUserForAuthPort; + + @BeforeEach + void setup(WebApplicationContext context, RestDocumentationContextProvider restDocumentation) { + this.mockMvc = + MockMvcBuilders.webAppContextSetup(context) + .apply(SecurityMockMvcConfigurers.springSecurity()) // secrutiy filter 적용 + .apply(documentationConfiguration(restDocumentation)) + .build(); + } + + // 블필요한 header 제거 함수 + protected HeadersModifyingOperationPreprocessor getModifiedHeader() { + return modifyHeaders() + .remove("X-Content-Type-Options") + .remove("X-XSS-Protection") + .remove("Cache-Control") + .remove("Pragma") + .remove("Expires") + .remove("Content-Length") + .remove("X-Frame-Options") + .remove("Vary"); + } + + // 사용자 생성 및 저장 + protected User createTestUser(String email, String password) { + String nickname = "test " + UUID.randomUUID(); + User user = + User.createGeneralUser( + GeneralUserCreationCommand.of( + email, + password, + nickname, + AgeGroup.FIFTIES, + List.of(HashTag.PERFUME))); + User testUser = saveUserPort.saveUser(user); + saveUserImagePort.saveUserDefaultImage(UserImage.createUserImage(testUser.getId())); + return testUser; + } + + protected MockHttpSession createUserAndLogin() { + return createUserAndLogin("test@gmail.com", "123456qwe!"); + } + + // test 사용자 생성 후 mock session 생성 + protected MockHttpSession createUserAndLogin(String email, String password) { + + // 사용자 생성 + User user = createTestUser(email, password); + + // session 생성 + SecurityContext context = SecurityContextHolder.createEmptyContext(); + UsernamePasswordAuthenticationToken auth = + new UsernamePasswordAuthenticationToken( + UserPrincipal.of(user), + null, + List.of(new SimpleGrantedAuthority(UserRole.USER.name()))); + context.setAuthentication(auth); + + MockHttpSession session = new MockHttpSession(); + session.setAttribute( + HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context); + + return session; + } + + protected MockHttpSession login(String email) { + User user = loadUserForAuthPort.loadUserByEmail(FindByEmailQuery.of(email)).get(); + + // session 생성 + SecurityContext context = SecurityContextHolder.createEmptyContext(); + UsernamePasswordAuthenticationToken auth = + new UsernamePasswordAuthenticationToken( + UserPrincipal.of(user), + null, + List.of(new SimpleGrantedAuthority(UserRole.USER.name()))); + context.setAuthentication(auth); + + MockHttpSession session = new MockHttpSession(); + session.setAttribute( + HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context); + + return session; + } + + public record SessionAndUser(MockHttpSession mockHttpSession, User user) {} + + // Session과 함께 User도 반환 + protected SessionAndUser createUserAndLoginAndReturnUser() { + return createUserAndLoginAndReturnUser("test@gmail.com", "123456qwe!"); + } + + protected SessionAndUser createUserAndLoginAndReturnUser(String email, String password) { + + // 사용자 생성 + User user = createTestUser(email, password); + + // session 생성 + SecurityContext context = SecurityContextHolder.createEmptyContext(); + UsernamePasswordAuthenticationToken auth = + new UsernamePasswordAuthenticationToken( + UserPrincipal.of(user), + null, + List.of(new SimpleGrantedAuthority(UserRole.USER.name()))); + context.setAuthentication(auth); + + MockHttpSession session = new MockHttpSession(); + session.setAttribute( + HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context); + + return new SessionAndUser(session, user); + } + + protected MockHttpSession createAdminUserAndLogin() { + String email = "admin@gmail.com"; + String password = "admin1234!"; + String nickname = "admintest"; + + User admin = User.createAdminUser(email, password, nickname); + + // session 생성 + SecurityContext context = SecurityContextHolder.createEmptyContext(); + UsernamePasswordAuthenticationToken auth = + new UsernamePasswordAuthenticationToken( + UserPrincipal.of(admin), + null, + List.of(new SimpleGrantedAuthority("ROLE_" + UserRole.ADMIN.name()))); + context.setAuthentication(auth); + + MockHttpSession session = new MockHttpSession(); + session.setAttribute( + HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context); + + return session; + } +} diff --git a/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250407231748.java b/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250407231748.java new file mode 100644 index 0000000..d8a08a2 --- /dev/null +++ b/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250407231748.java @@ -0,0 +1,89 @@ +package com.ftm.server.user; + +import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.ftm.server.BaseTest; +import com.ftm.server.adapter.in.web.user.dto.request.EmailCodeVerificationRequest; +import com.ftm.server.application.command.user.EmailVerificationLogCreationCommand; +import com.ftm.server.application.port.out.persistence.user.SaveEmailVerificationLogPort; +import jakarta.transaction.Transactional; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.restdocs.payload.JsonFieldType; +import org.springframework.test.web.servlet.ResultActions; + +public class EmailCodeVerificationTest extends BaseTest { + + @Autowired private SaveEmailVerificationLogPort saveEmailVerificationLogPort; + + private final List requestFieldDescriptors = + List.of( + fieldWithPath("email").type(JsonFieldType.STRING).description("인증 email"), + fieldWithPath("code").type(JsonFieldType.STRING).description("인증 코드")); + + private final List responseFieldDescriptors = + List.of( + fieldWithPath("status").type(JsonFieldType.NUMBER).description("응답 상태"), + fieldWithPath("code").type(JsonFieldType.STRING).description("상태 코드"), + fieldWithPath("message").type(JsonFieldType.STRING).description("메시지"), + fieldWithPath("data").type(JsonFieldType.OBJECT).optional().description("data"), + fieldWithPath("data.isVerified") + .type(JsonFieldType.BOOLEAN) + .description("검증 성공 여부")); + + private ResultActions getResultActions(EmailCodeVerificationRequest request) throws Exception { + return mockMvc.perform( // api 실행 + RestDocumentationRequestBuilders.post("/api/users/email/authentication/code") + .contentType(MediaType.APPLICATION_JSON) // request body content type + .content(mapper.writeValueAsString(request))); + } + + // 문서화 반환 함수 + private RestDocumentationResultHandler getDocument(Integer identifier) { + return document( + "emailCodeVerification/" + identifier, + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint(), getModifiedHeader()), + responseFields(responseFieldDescriptors), + requestFields(requestFieldDescriptors), + resource( + ResourceSnippetParameters.builder() + .tag("회원") + .summary("이메일 인증 코드 검증 api") + .description("이메일 인증 코드를 검증하는 api입니다.") + .responseFields(responseFieldDescriptors) + .requestFields(requestFieldDescriptors) + .build())); + } + + @Test + @Transactional + void 이메일_인증코드_검증_성공() throws Exception { + + String email = "test@gmail.com"; + String code = "123456"; + // given + saveEmailVerificationLogPort.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, code)); + + // when + ResultActions resultActions = + getResultActions(new EmailCodeVerificationRequest(email, code)); + + // then + resultActions.andExpect(status().isOk()); + + // documentation + resultActions.andDo(getDocument(1)); + } +} diff --git a/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622204717.java b/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622204717.java new file mode 100644 index 0000000..da02fc6 --- /dev/null +++ b/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622204717.java @@ -0,0 +1,92 @@ +package com.ftm.server.user; + +import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.ftm.server.BaseTest; +import com.ftm.server.adapter.in.web.user.dto.request.EmailCodeVerificationRequest; +import com.ftm.server.application.command.user.EmailVerificationLogCreationCommand; +import com.ftm.server.application.port.out.persistence.user.SaveEmailVerificationLogPort; +import jakarta.transaction.Transactional; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.restdocs.payload.JsonFieldType; +import org.springframework.test.web.servlet.ResultActions; + +public class EmailCodeVerificationTest extends BaseTest { + + @Autowired private SaveEmailVerificationLogPort saveEmailVerificationLogPort; + + private final List requestFieldDescriptors = + List.of( + fieldWithPath("email").type(JsonFieldType.STRING).description("인증 email"), + fieldWithPath("code").type(JsonFieldType.STRING).description("인증 코드")); + + private final List responseFieldDescriptors = + List.of( + fieldWithPath("status").type(JsonFieldType.NUMBER).description("응답 상태"), + fieldWithPath("code").type(JsonFieldType.STRING).description("상태 코드"), + fieldWithPath("message").type(JsonFieldType.STRING).description("메시지"), + fieldWithPath("data").type(JsonFieldType.OBJECT).optional().description("data"), + fieldWithPath("data.isVerified") + .type(JsonFieldType.BOOLEAN) + .description("검증 성공 여부"), + fieldWithPath("data.isRecoverable") + .type(JsonFieldType.BOOLEAN) + .description("계정 복구 가능 여부 (soft delete 상태인지)")); + + private ResultActions getResultActions(EmailCodeVerificationRequest request) throws Exception { + return mockMvc.perform( // api 실행 + RestDocumentationRequestBuilders.post("/api/users/email/authentication/code") + .contentType(MediaType.APPLICATION_JSON) // request body content type + .content(mapper.writeValueAsString(request))); + } + + // 문서화 반환 함수 + private RestDocumentationResultHandler getDocument(Integer identifier) { + return document( + "emailCodeVerification/" + identifier, + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint(), getModifiedHeader()), + responseFields(responseFieldDescriptors), + requestFields(requestFieldDescriptors), + resource( + ResourceSnippetParameters.builder() + .tag("회원") + .summary("이메일 인증 코드 검증 api") + .description("이메일 인증 코드를 검증하는 api입니다.") + .responseFields(responseFieldDescriptors) + .requestFields(requestFieldDescriptors) + .build())); + } + + @Test + @Transactional + void 이메일_인증코드_검증_성공() throws Exception { + + String email = "test@gmail.com"; + String code = "123456"; + // given + saveEmailVerificationLogPort.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, code)); + + // when + ResultActions resultActions = + getResultActions(new EmailCodeVerificationRequest(email, code)); + + // then + resultActions.andExpect(status().isOk()); + + // documentation + resultActions.andDo(getDocument(1)); + } +} diff --git a/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622205425.java b/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622205425.java new file mode 100644 index 0000000..da02fc6 --- /dev/null +++ b/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622205425.java @@ -0,0 +1,92 @@ +package com.ftm.server.user; + +import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.ftm.server.BaseTest; +import com.ftm.server.adapter.in.web.user.dto.request.EmailCodeVerificationRequest; +import com.ftm.server.application.command.user.EmailVerificationLogCreationCommand; +import com.ftm.server.application.port.out.persistence.user.SaveEmailVerificationLogPort; +import jakarta.transaction.Transactional; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.restdocs.payload.JsonFieldType; +import org.springframework.test.web.servlet.ResultActions; + +public class EmailCodeVerificationTest extends BaseTest { + + @Autowired private SaveEmailVerificationLogPort saveEmailVerificationLogPort; + + private final List requestFieldDescriptors = + List.of( + fieldWithPath("email").type(JsonFieldType.STRING).description("인증 email"), + fieldWithPath("code").type(JsonFieldType.STRING).description("인증 코드")); + + private final List responseFieldDescriptors = + List.of( + fieldWithPath("status").type(JsonFieldType.NUMBER).description("응답 상태"), + fieldWithPath("code").type(JsonFieldType.STRING).description("상태 코드"), + fieldWithPath("message").type(JsonFieldType.STRING).description("메시지"), + fieldWithPath("data").type(JsonFieldType.OBJECT).optional().description("data"), + fieldWithPath("data.isVerified") + .type(JsonFieldType.BOOLEAN) + .description("검증 성공 여부"), + fieldWithPath("data.isRecoverable") + .type(JsonFieldType.BOOLEAN) + .description("계정 복구 가능 여부 (soft delete 상태인지)")); + + private ResultActions getResultActions(EmailCodeVerificationRequest request) throws Exception { + return mockMvc.perform( // api 실행 + RestDocumentationRequestBuilders.post("/api/users/email/authentication/code") + .contentType(MediaType.APPLICATION_JSON) // request body content type + .content(mapper.writeValueAsString(request))); + } + + // 문서화 반환 함수 + private RestDocumentationResultHandler getDocument(Integer identifier) { + return document( + "emailCodeVerification/" + identifier, + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint(), getModifiedHeader()), + responseFields(responseFieldDescriptors), + requestFields(requestFieldDescriptors), + resource( + ResourceSnippetParameters.builder() + .tag("회원") + .summary("이메일 인증 코드 검증 api") + .description("이메일 인증 코드를 검증하는 api입니다.") + .responseFields(responseFieldDescriptors) + .requestFields(requestFieldDescriptors) + .build())); + } + + @Test + @Transactional + void 이메일_인증코드_검증_성공() throws Exception { + + String email = "test@gmail.com"; + String code = "123456"; + // given + saveEmailVerificationLogPort.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, code)); + + // when + ResultActions resultActions = + getResultActions(new EmailCodeVerificationRequest(email, code)); + + // then + resultActions.andExpect(status().isOk()); + + // documentation + resultActions.andDo(getDocument(1)); + } +} diff --git a/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622205721.java b/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622205721.java new file mode 100644 index 0000000..bc60406 --- /dev/null +++ b/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622205721.java @@ -0,0 +1,151 @@ +package com.ftm.server.user; + +import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.ftm.server.BaseTest; +import com.ftm.server.adapter.in.web.user.dto.request.EmailCodeVerificationRequest; +import com.ftm.server.application.command.user.EmailVerificationLogCreationCommand; +import com.ftm.server.application.port.out.persistence.user.SaveEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.SaveUserPort; +import com.ftm.server.domain.entity.User; +import jakarta.transaction.Transactional; +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.restdocs.payload.JsonFieldType; +import org.springframework.test.web.servlet.ResultActions; + +public class EmailCodeVerificationTest extends BaseTest { + + @Autowired private SaveEmailVerificationLogPort saveEmailVerificationLogPort; + @Autowired private SaveUserPort saveUserPort; + + private final List requestFieldDescriptors = + List.of( + fieldWithPath("email").type(JsonFieldType.STRING).description("인증 email"), + fieldWithPath("code").type(JsonFieldType.STRING).description("인증 코드")); + + private final List responseFieldDescriptors = + List.of( + fieldWithPath("status").type(JsonFieldType.NUMBER).description("응답 상태"), + fieldWithPath("code").type(JsonFieldType.STRING).description("상태 코드"), + fieldWithPath("message").type(JsonFieldType.STRING).description("메시지"), + fieldWithPath("data").type(JsonFieldType.OBJECT).optional().description("data"), + fieldWithPath("data.isVerified") + .type(JsonFieldType.BOOLEAN) + .description("검증 성공 여부"), + fieldWithPath("data.isRecoverable") + .type(JsonFieldType.BOOLEAN) + .description("계정 복구 가능 여부")); + + private ResultActions getResultActions(EmailCodeVerificationRequest request) throws Exception { + return mockMvc.perform( // api 실행 + RestDocumentationRequestBuilders.post("/api/users/email/authentication/code") + .contentType(MediaType.APPLICATION_JSON) // request body content type + .content(mapper.writeValueAsString(request))); + } + + // 문서화 반환 함수 + private RestDocumentationResultHandler getDocument(Integer identifier) { + return document( + "emailCodeVerification/" + identifier, + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint(), getModifiedHeader()), + responseFields(responseFieldDescriptors), + requestFields(requestFieldDescriptors), + resource( + ResourceSnippetParameters.builder() + .tag("회원") + .summary("이메일 인증 코드 검증 api") + .description("이메일 인증 코드를 검증하는 api입니다.") + .responseFields(responseFieldDescriptors) + .requestFields(requestFieldDescriptors) + .build())); + } + + @Test + @Transactional + void 이메일_인증코드_검증_성공() throws Exception { + + String email = "test@gmail.com"; + String code = "123456"; + // given + saveEmailVerificationLogPort.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, code)); + + // when + ResultActions resultActions = + getResultActions(new EmailCodeVerificationRequest(email, code)); + + // then + resultActions.andExpect(status().isOk()); + + // documentation + resultActions.andDo(getDocument(1)); + } + + @Test + @Transactional + void 이메일_인증코드_검증_실패() throws Exception { + + String email = "test@gmail.com"; + String correctCode = "123456"; + String wrongCode = "654321"; + + // given + saveEmailVerificationLogPort.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, correctCode)); + + // when + ResultActions resultActions = + getResultActions(new EmailCodeVerificationRequest(email, wrongCode)); + + // then + resultActions + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.isVerified").value(false)) + .andExpect(jsonPath("$.data.isRecoverable").value(false)); + + // documentation + resultActions.andDo(getDocument(2)); + } + + @Test + @Transactional + void 이메일_인증코드_검증_성공_soft_delete_사용자_복구_가능() throws Exception { + + String email = "softdeleted@gmail.com"; + String code = "123456"; + + // given - soft delete된 사용자 생성 + User softDeletedUser = createSoftDeletedUser(email, "password123!"); + + // given - 이메일 인증 로그 생성 + saveEmailVerificationLogPort.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, code)); + + // when + ResultActions resultActions = + getResultActions(new EmailCodeVerificationRequest(email, code)); + + // then + resultActions + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.isVerified").value(true)) + .andExpect(jsonPath("$.data.isRecoverable").value(true)); + + // documentation + resultActions.andDo(getDocument(3)); + } +} diff --git a/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622210254.java b/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622210254.java new file mode 100644 index 0000000..27d1629 --- /dev/null +++ b/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622210254.java @@ -0,0 +1,160 @@ +package com.ftm.server.user; + +import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.ftm.server.BaseTest; +import com.ftm.server.adapter.in.web.user.dto.request.EmailCodeVerificationRequest; +import com.ftm.server.application.command.user.EmailVerificationLogCreationCommand; +import com.ftm.server.application.port.out.persistence.user.SaveEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.SaveUserPort; +import com.ftm.server.domain.entity.User; +import jakarta.transaction.Transactional; +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.restdocs.payload.JsonFieldType; +import org.springframework.test.web.servlet.ResultActions; + +public class EmailCodeVerificationTest extends BaseTest { + + @Autowired private SaveEmailVerificationLogPort saveEmailVerificationLogPort; + @Autowired private SaveUserPort saveUserPort; + + private final List requestFieldDescriptors = + List.of( + fieldWithPath("email").type(JsonFieldType.STRING).description("인증 email"), + fieldWithPath("code").type(JsonFieldType.STRING).description("인증 코드")); + + private final List responseFieldDescriptors = + List.of( + fieldWithPath("status").type(JsonFieldType.NUMBER).description("응답 상태"), + fieldWithPath("code").type(JsonFieldType.STRING).description("상태 코드"), + fieldWithPath("message").type(JsonFieldType.STRING).description("메시지"), + fieldWithPath("data").type(JsonFieldType.OBJECT).optional().description("data"), + fieldWithPath("data.isVerified") + .type(JsonFieldType.BOOLEAN) + .description("검증 성공 여부"), + fieldWithPath("data.isRecoverable") + .type(JsonFieldType.BOOLEAN) + .description("계정 복구 가능 여부")); + + private ResultActions getResultActions(EmailCodeVerificationRequest request) throws Exception { + return mockMvc.perform( // api 실행 + RestDocumentationRequestBuilders.post("/api/users/email/authentication/code") + .contentType(MediaType.APPLICATION_JSON) // request body content type + .content(mapper.writeValueAsString(request))); + } + + // 문서화 반환 함수 + private RestDocumentationResultHandler getDocument(Integer identifier) { + return document( + "emailCodeVerification/" + identifier, + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint(), getModifiedHeader()), + responseFields(responseFieldDescriptors), + requestFields(requestFieldDescriptors), + resource( + ResourceSnippetParameters.builder() + .tag("회원") + .summary("이메일 인증 코드 검증 api") + .description("이메일 인증 코드를 검증하는 api입니다.") + .responseFields(responseFieldDescriptors) + .requestFields(requestFieldDescriptors) + .build())); + } + + protected User createSoftDeletedUser(String email, String password) { + User user = createTestUser(email, password); + user.updateIsDeleted(true); + user.updateDeletedAt(LocalDateTime.now()); + saveUserPort.saveUser(user); + return user; + } + + + @Test + @Transactional + void 이메일_인증코드_검증_성공() throws Exception { + + String email = "test@gmail.com"; + String code = "123456"; + // given + saveEmailVerificationLogPort.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, code)); + + // when + ResultActions resultActions = + getResultActions(new EmailCodeVerificationRequest(email, code)); + + // then + resultActions.andExpect(status().isOk()); + + // documentation + resultActions.andDo(getDocument(1)); + } + + @Test + @Transactional + void 이메일_인증코드_검증_실패() throws Exception { + + String email = "test@gmail.com"; + String correctCode = "123456"; + String wrongCode = "654321"; + + // given + saveEmailVerificationLogPort.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, correctCode)); + + // when + ResultActions resultActions = + getResultActions(new EmailCodeVerificationRequest(email, wrongCode)); + + // then + resultActions + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.isVerified").value(false)) + .andExpect(jsonPath("$.data.isRecoverable").value(false)); + + // documentation + resultActions.andDo(getDocument(2)); + } + + @Test + @Transactional + void 이메일_인증코드_검증_성공_soft_delete_사용자_복구_가능() throws Exception { + + String email = "softdeleted@gmail.com"; + String code = "123456"; + + // given - soft delete된 사용자 생성 + User softDeletedUser = createSoftDeletedUser(email, "password123!"); + + // given - 이메일 인증 로그 생성 + saveEmailVerificationLogPort.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, code)); + + // when + ResultActions resultActions = + getResultActions(new EmailCodeVerificationRequest(email, code)); + + // then + resultActions + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.isVerified").value(true)) + .andExpect(jsonPath("$.data.isRecoverable").value(true)); + + // documentation + resultActions.andDo(getDocument(3)); + } +} diff --git a/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622211329.java b/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622211329.java new file mode 100644 index 0000000..92d499b --- /dev/null +++ b/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622211329.java @@ -0,0 +1,134 @@ +package com.ftm.server.user; + +import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.ftm.server.BaseTest; +import com.ftm.server.adapter.in.web.user.dto.request.EmailCodeVerificationRequest; +import com.ftm.server.application.command.user.EmailVerificationLogCreationCommand; +import com.ftm.server.application.port.out.persistence.user.SaveEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.SaveUserPort; +import com.ftm.server.domain.entity.User; +import jakarta.transaction.Transactional; +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.restdocs.payload.JsonFieldType; +import org.springframework.test.web.servlet.ResultActions; + +public class EmailCodeVerificationTest extends BaseTest { + + @Autowired private SaveEmailVerificationLogPort saveEmailVerificationLogPort; + @Autowired private SaveUserPort saveUserPort; + + private final List requestFieldDescriptors = + List.of( + fieldWithPath("email").type(JsonFieldType.STRING).description("인증 email"), + fieldWithPath("code").type(JsonFieldType.STRING).description("인증 코드")); + + private final List responseFieldDescriptors = + List.of( + fieldWithPath("status").type(JsonFieldType.NUMBER).description("응답 상태"), + fieldWithPath("code").type(JsonFieldType.STRING).description("상태 코드"), + fieldWithPath("message").type(JsonFieldType.STRING).description("메시지"), + fieldWithPath("data").type(JsonFieldType.OBJECT).optional().description("data"), + fieldWithPath("data.isVerified") + .type(JsonFieldType.BOOLEAN) + .description("검증 성공 여부"), + fieldWithPath("data.isRecoverable") + .type(JsonFieldType.BOOLEAN) + .description("계정 복구 가능 여부")); + + private ResultActions getResultActions(EmailCodeVerificationRequest request) throws Exception { + return mockMvc.perform( // api 실행 + RestDocumentationRequestBuilders.post("/api/users/email/authentication/code") + .contentType(MediaType.APPLICATION_JSON) // request body content type + .content(mapper.writeValueAsString(request))); + } + + // 문서화 반환 함수 + private RestDocumentationResultHandler getDocument(Integer identifier) { + return document( + "emailCodeVerification/" + identifier, + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint(), getModifiedHeader()), + responseFields(responseFieldDescriptors), + requestFields(requestFieldDescriptors), + resource( + ResourceSnippetParameters.builder() + .tag("회원") + .summary("이메일 인증 코드 검증 api") + .description("이메일 인증 코드를 검증하는 api입니다.") + .responseFields(responseFieldDescriptors) + .requestFields(requestFieldDescriptors) + .build())); + } + + protected User createSoftDeletedUser(String email, String password) { + User user = createTestUser(email, password); + user.updateIsDeleted(true); + user.updateDeletedAt(LocalDateTime.now()); + saveUserPort.saveUser(user); + return user; + } + + + @Test + @Transactional + void 이메일_인증코드_검증_성공() throws Exception { + + String email = "test@gmail.com"; + String code = "123456"; + // given + saveEmailVerificationLogPort.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, code)); + + // when + ResultActions resultActions = + getResultActions(new EmailCodeVerificationRequest(email, code)); + + // then + resultActions.andExpect(status().isOk()); + + // documentation + resultActions.andDo(getDocument(1)); + } + + @Test + @Transactional + void 이메일_인증코드_검증_실패() throws Exception { + + String email = "test@gmail.com"; + String correctCode = "123456"; + String wrongCode = "654321"; + + // given + saveEmailVerificationLogPort.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, correctCode)); + + // when + ResultActions resultActions = + getResultActions(new EmailCodeVerificationRequest(email, wrongCode)); + + // then + resultActions + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.isVerified").value(false)) + .andExpect(jsonPath("$.data.isRecoverable").value(false)); + + // documentation + resultActions.andDo(getDocument(2)); + } + + +} diff --git a/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622213651.java b/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622213651.java new file mode 100644 index 0000000..38b48f5 --- /dev/null +++ b/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622213651.java @@ -0,0 +1,202 @@ +package com.ftm.server.user; + +import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.ftm.server.BaseTest; +import com.ftm.server.adapter.in.web.user.dto.request.EmailCodeVerificationRequest; +import com.ftm.server.application.command.user.EmailVerificationLogCreationCommand; +import com.ftm.server.application.port.out.persistence.user.SaveEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.SaveUserPort; +import com.ftm.server.domain.entity.User; +import jakarta.transaction.Transactional; +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.restdocs.payload.JsonFieldType; +import org.springframework.test.web.servlet.ResultActions; + +public class EmailCodeVerificationTest extends BaseTest { + + @Autowired private SaveEmailVerificationLogPort saveEmailVerificationLogPort; + @Autowired private SaveUserPort saveUserPort; + + private final List requestFieldDescriptors = + List.of( + fieldWithPath("email").type(JsonFieldType.STRING).description("인증 email"), + fieldWithPath("code").type(JsonFieldType.STRING).description("인증 코드")); + + private final List responseFieldDescriptors = + List.of( + fieldWithPath("status").type(JsonFieldType.NUMBER).description("응답 상태"), + fieldWithPath("code").type(JsonFieldType.STRING).description("상태 코드"), + fieldWithPath("message").type(JsonFieldType.STRING).description("메시지"), + fieldWithPath("data").type(JsonFieldType.OBJECT).optional().description("data"), + fieldWithPath("data.isVerified") + .type(JsonFieldType.BOOLEAN) + .description("검증 성공 여부"), + fieldWithPath("data.isRecoverable") + .type(JsonFieldType.BOOLEAN) + .description("계정 복구 가능 여부")); + + private ResultActions getResultActions(EmailCodeVerificationRequest request) throws Exception { + return mockMvc.perform( // api 실행 + RestDocumentationRequestBuilders.post("/api/users/email/authentication/code") + .contentType(MediaType.APPLICATION_JSON) // request body content type + .content(mapper.writeValueAsString(request))); + } + + // 문서화 반환 함수 + private RestDocumentationResultHandler getDocument(Integer identifier) { + return document( + "emailCodeVerification/" + identifier, + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint(), getModifiedHeader()), + responseFields(responseFieldDescriptors), + requestFields(requestFieldDescriptors), + resource( + ResourceSnippetParameters.builder() + .tag("회원") + .summary("이메일 인증 코드 검증 api") + .description("이메일 인증 코드를 검증하는 api입니다.") + .responseFields(responseFieldDescriptors) + .requestFields(requestFieldDescriptors) + .build())); + } + + protected User createSoftDeletedUser(String email, String password) { + User user = createTestUser(email, password); + user.updateIsDeleted(true); + user.updateDeletedAt(LocalDateTime.now()); + saveUserPort.saveUser(user); + return user; + } + + + @Test + @Transactional + void 이메일_인증코드_검증_성공() throws Exception { + + String email = "test@gmail.com"; + String code = "123456"; + // given + saveEmailVerificationLogPort.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, code)); + + // when + ResultActions resultActions = + getResultActions(new EmailCodeVerificationRequest(email, code)); + + // then + resultActions.andExpect(status().isOk()); + + // documentation + resultActions.andDo(getDocument(1)); + } + + @Test + @Transactional + void 이메일_인증코드_검증_실패() throws Exception { + + String email = "test@gmail.com"; + String correctCode = "123456"; + String wrongCode = "654321"; + + // given + saveEmailVerificationLogPort.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, correctCode)); + + // when + ResultActions resultActions = + getResultActions(new EmailCodeVerificationRequest(email, wrongCode)); + + // then + resultActions + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.isVerified").value(false)) + .andExpect(jsonPath("$.data.isRecoverable").value(false)); + + // documentation + resultActions.andDo(getDocument(2)); + } + + @Test + @Transactional + void 이메일_인증코드_검증_성공_soft_delete_사용자_복구_가능() throws Exception { + + String email = "softdeleted@gmail.com"; + String code = "123456"; + + // given - soft delete된 사용자 생성 + User softDeletedUser = createSoftDeletedUser(email, "password123!"); + + // given - 이메일 인증 로그 생성 + saveEmailVerificationLogPort.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, code)); + + // when + ResultActions resultActions = + getResultActions(new EmailCodeVerificationRequest(email, code)); + + // then + resultActions + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.isVerified").value(true)) + .andExpect(jsonPath("$.data.isRecoverable").value(true)); + + // documentation + resultActions.andDo(getDocument(3)); + } + + @Test + @Transactional + void 회원가입_완료_후_이메일_인증_로그_삭제_확인() throws Exception { + + String email = "signup@gmail.com"; + String code = "123456"; + + // given - 이메일 인증 로그 생성 + saveEmailVerificationLogPort.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, code)); + + // given - 이메일 인증 코드 검증 + getResultActions(new EmailCodeVerificationRequest(email, code)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.isVerified").value(true)); + + // when - 회원가입 진행 + GeneralUserSignupRequest signupRequest = new GeneralUserSignupRequest( + email, "password123!", AgeGroup.TWENTIES, List.of(HashTag.PERFUME)); + + ResultActions signupResult = mockMvc.perform( + RestDocumentationRequestBuilders.post("/api/users/signup") + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsString(signupRequest))); + + // then - 회원가입 성공 + signupResult.andExpect(status().isCreated()); + + // then - 이메일 인증 로그가 삭제되었는지 확인 (다시 인증 코드 검증 시도) + ResultActions verificationResult = + getResultActions(new EmailCodeVerificationRequest(email, code)); + + verificationResult + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.isVerified").value(false)) + .andExpect(jsonPath("$.data.isRecoverable").value(false)); + + // documentation + verificationResult.andDo(getDocument(4)); + } + +} diff --git a/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622213710.java b/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622213710.java new file mode 100644 index 0000000..fd191ce --- /dev/null +++ b/.history/src/test/java/com/ftm/server/user/EmailCodeVerificationTest_20250622213710.java @@ -0,0 +1,205 @@ +package com.ftm.server.user; + +import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.ftm.server.BaseTest; +import com.ftm.server.adapter.in.web.user.dto.request.EmailCodeVerificationRequest; +import com.ftm.server.adapter.in.web.user.dto.request.GeneralUserSignupRequest; +import com.ftm.server.application.command.user.EmailVerificationLogCreationCommand; +import com.ftm.server.application.port.out.persistence.user.SaveEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.SaveUserPort; +import com.ftm.server.domain.entity.User; +import com.ftm.server.domain.enums.AgeGroup; +import com.ftm.server.domain.enums.HashTag; +import jakarta.transaction.Transactional; +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.restdocs.payload.JsonFieldType; +import org.springframework.test.web.servlet.ResultActions; + +public class EmailCodeVerificationTest extends BaseTest { + + @Autowired private SaveEmailVerificationLogPort saveEmailVerificationLogPort; + @Autowired private SaveUserPort saveUserPort; + + private final List requestFieldDescriptors = + List.of( + fieldWithPath("email").type(JsonFieldType.STRING).description("인증 email"), + fieldWithPath("code").type(JsonFieldType.STRING).description("인증 코드")); + + private final List responseFieldDescriptors = + List.of( + fieldWithPath("status").type(JsonFieldType.NUMBER).description("응답 상태"), + fieldWithPath("code").type(JsonFieldType.STRING).description("상태 코드"), + fieldWithPath("message").type(JsonFieldType.STRING).description("메시지"), + fieldWithPath("data").type(JsonFieldType.OBJECT).optional().description("data"), + fieldWithPath("data.isVerified") + .type(JsonFieldType.BOOLEAN) + .description("검증 성공 여부"), + fieldWithPath("data.isRecoverable") + .type(JsonFieldType.BOOLEAN) + .description("계정 복구 가능 여부")); + + private ResultActions getResultActions(EmailCodeVerificationRequest request) throws Exception { + return mockMvc.perform( // api 실행 + RestDocumentationRequestBuilders.post("/api/users/email/authentication/code") + .contentType(MediaType.APPLICATION_JSON) // request body content type + .content(mapper.writeValueAsString(request))); + } + + // 문서화 반환 함수 + private RestDocumentationResultHandler getDocument(Integer identifier) { + return document( + "emailCodeVerification/" + identifier, + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint(), getModifiedHeader()), + responseFields(responseFieldDescriptors), + requestFields(requestFieldDescriptors), + resource( + ResourceSnippetParameters.builder() + .tag("회원") + .summary("이메일 인증 코드 검증 api") + .description("이메일 인증 코드를 검증하는 api입니다.") + .responseFields(responseFieldDescriptors) + .requestFields(requestFieldDescriptors) + .build())); + } + + protected User createSoftDeletedUser(String email, String password) { + User user = createTestUser(email, password); + user.updateIsDeleted(true); + user.updateDeletedAt(LocalDateTime.now()); + saveUserPort.saveUser(user); + return user; + } + + + @Test + @Transactional + void 이메일_인증코드_검증_성공() throws Exception { + + String email = "test@gmail.com"; + String code = "123456"; + // given + saveEmailVerificationLogPort.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, code)); + + // when + ResultActions resultActions = + getResultActions(new EmailCodeVerificationRequest(email, code)); + + // then + resultActions.andExpect(status().isOk()); + + // documentation + resultActions.andDo(getDocument(1)); + } + + @Test + @Transactional + void 이메일_인증코드_검증_실패() throws Exception { + + String email = "test@gmail.com"; + String correctCode = "123456"; + String wrongCode = "654321"; + + // given + saveEmailVerificationLogPort.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, correctCode)); + + // when + ResultActions resultActions = + getResultActions(new EmailCodeVerificationRequest(email, wrongCode)); + + // then + resultActions + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.isVerified").value(false)) + .andExpect(jsonPath("$.data.isRecoverable").value(false)); + + // documentation + resultActions.andDo(getDocument(2)); + } + + @Test + @Transactional + void 이메일_인증코드_검증_성공_soft_delete_사용자_복구_가능() throws Exception { + + String email = "softdeleted@gmail.com"; + String code = "123456"; + + // given - soft delete된 사용자 생성 + User softDeletedUser = createSoftDeletedUser(email, "password123!"); + + // given - 이메일 인증 로그 생성 + saveEmailVerificationLogPort.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, code)); + + // when + ResultActions resultActions = + getResultActions(new EmailCodeVerificationRequest(email, code)); + + // then + resultActions + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.isVerified").value(true)) + .andExpect(jsonPath("$.data.isRecoverable").value(true)); + + // documentation + resultActions.andDo(getDocument(3)); + } + + @Test + @Transactional + void 회원가입_완료_후_이메일_인증_로그_삭제_확인() throws Exception { + + String email = "signup@gmail.com"; + String code = "123456"; + + // given - 이메일 인증 로그 생성 + saveEmailVerificationLogPort.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, code)); + + // given - 이메일 인증 코드 검증 + getResultActions(new EmailCodeVerificationRequest(email, code)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.isVerified").value(true)); + + // when - 회원가입 진행 + GeneralUserSignupRequest signupRequest = new GeneralUserSignupRequest( + email, "password123!", AgeGroup.TWENTIES, List.of(HashTag.PERFUME)); + + ResultActions signupResult = mockMvc.perform( + RestDocumentationRequestBuilders.post("/api/users/signup") + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsString(signupRequest))); + + // then - 회원가입 성공 + signupResult.andExpect(status().isCreated()); + + // then - 이메일 인증 로그가 삭제되었는지 확인 (다시 인증 코드 검증 시도) + ResultActions verificationResult = + getResultActions(new EmailCodeVerificationRequest(email, code)); + + verificationResult + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.isVerified").value(false)) + .andExpect(jsonPath("$.data.isRecoverable").value(false)); + + // documentation + verificationResult.andDo(getDocument(4)); + } + +} diff --git a/src/main/java/com/ftm/server/adapter/in/web/user/dto/response/EmailCodeVerificationResponse.java b/src/main/java/com/ftm/server/adapter/in/web/user/dto/response/EmailCodeVerificationResponse.java index 034acd6..26fe9de 100644 --- a/src/main/java/com/ftm/server/adapter/in/web/user/dto/response/EmailCodeVerificationResponse.java +++ b/src/main/java/com/ftm/server/adapter/in/web/user/dto/response/EmailCodeVerificationResponse.java @@ -6,8 +6,9 @@ @Data public class EmailCodeVerificationResponse { private final Boolean isVerified; + private final Boolean isRecoverable; public static EmailCodeVerificationResponse from(EmailCodeVerificationVo vo) { - return new EmailCodeVerificationResponse(vo.getIsVerified()); + return new EmailCodeVerificationResponse(vo.getIsVerified(), vo.getIsRecoverable()); } } diff --git a/src/main/java/com/ftm/server/adapter/out/cache/LoadTrendingPostsTestAdapter.java b/src/main/java/com/ftm/server/adapter/out/cache/LoadTrendingPostsTestAdapter.java new file mode 100644 index 0000000..1b61aaf --- /dev/null +++ b/src/main/java/com/ftm/server/adapter/out/cache/LoadTrendingPostsTestAdapter.java @@ -0,0 +1,87 @@ +package com.ftm.server.adapter.out.cache; + +import com.ftm.server.application.port.out.cache.LoadTrendingPostsWithCachePort; +import com.ftm.server.application.port.out.persistence.post.LoadPostImagePort; +import com.ftm.server.application.port.out.persistence.post.LoadPostWithBookmarkCountPort; +import com.ftm.server.application.query.FindByIdsQuery; +import com.ftm.server.application.query.FindPostsByCreatedDateQuery; +import com.ftm.server.application.vo.post.PostWithBookmarkCountVo; +import com.ftm.server.application.vo.post.TrendingPostVo; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.IntStream; + +@Service +@RequiredArgsConstructor +@Primary +public class LoadTrendingPostsTestAdapter implements LoadTrendingPostsWithCachePort { + + private final LoadPostWithBookmarkCountPort loadPostPort; + private final LoadPostImagePort loadPostImagePort; + + private static final int N = 15; + + @Override + public List loadTrendingPosts() { + + // 현재보다 1주일 이전에 작성된 게시물만 조회(북마크 조회수 포함) (예전 게시물은 포함x) + List rawPosts = + loadPostPort.loadAllPostsWithBookmarkCount( + FindPostsByCreatedDateQuery.of(LocalDate.now())); + + if (rawPosts.isEmpty()) return List.of(); + + // 1. 최대값 계산 (정규화를 위해) + long maxView = 1, maxLike = 1, maxScrap = 1; + for (PostWithBookmarkCountVo post : rawPosts) { + maxView = Math.max(maxView, post.getViewCount()); + maxLike = Math.max(maxLike, post.getLikeCount()); + maxScrap = Math.max(maxScrap, post.getScrapCount()); + } + + // 2. 점수 계산 후 정렬 + long finalMaxView = maxView; + long finalMaxLike = maxLike; + long finalMaxScrap = maxScrap; + List topN = + rawPosts.stream() + .sorted( + Comparator.comparingDouble( + p -> + -((double) p.getViewCount() / finalMaxView + + (double) p.getLikeCount() + / finalMaxLike + + (double) p.getScrapCount() + / finalMaxScrap) + / 3.0)) + .limit(N) + .toList(); + + // 3. 각 게시물 이미지 조회 (없으면 null) + List postIds = topN.stream().map(PostWithBookmarkCountVo::getPostId).toList(); + + Map imageMap = new HashMap<>(); + postIds.forEach(p -> imageMap.put(p, null)); + loadPostImagePort + .loadRepresentativeImagesByPostIds(FindByIdsQuery.from(postIds)) + .forEach( + postImage -> imageMap.put(postImage.getPostId(), postImage.getObjectKey())); + + // 4. 결과 조합 (순위 부여) + return IntStream.range(0, topN.size()) + .mapToObj( + i -> { + PostWithBookmarkCountVo post = topN.get(i); + String imageKey = imageMap.get(post.getPostId()); + return TrendingPostVo.from(post, i + 1, imageKey); // rank = i+1 + }) + .toList(); + } +} diff --git a/src/main/java/com/ftm/server/adapter/out/persistence/adapter/user/UserDomainPersistenceAdapter.java b/src/main/java/com/ftm/server/adapter/out/persistence/adapter/user/UserDomainPersistenceAdapter.java index 283a8dc..c451e7f 100644 --- a/src/main/java/com/ftm/server/adapter/out/persistence/adapter/user/UserDomainPersistenceAdapter.java +++ b/src/main/java/com/ftm/server/adapter/out/persistence/adapter/user/UserDomainPersistenceAdapter.java @@ -25,6 +25,7 @@ public class UserDomainPersistenceAdapter implements LoadEmailVerificationLogPort, SaveEmailVerificationLogPort, UpdateEmailVerificationLogPort, + DeleteEmailVerificationLogPort, CheckUserPort, SaveUserPort, SaveUserImagePort, @@ -91,6 +92,11 @@ public void updateEmailVerificationLog(EmailVerificationLogs emailVerificationLo emailVerificationLogsJpaEntity.updateFromDomainEntity(emailVerificationLogs); } + @Override + public void deleteEmailVerificationLogsByEmail(DeleteByEmailCommand command) { + emailVerificationLogsRepository.deleteAllByEmail(command.getEmail()); + } + @Override public Boolean checksUserByEmail(FindByEmailQuery query) { return userRepository.existsByEmail(query.getEmail()); @@ -154,6 +160,12 @@ public List loadUserByDeleteOption(FindUserByDeleteOptionQuery query) { .toList(); } + @Override + public Optional loadDeletedUserByEmail(FindByEmailQuery query) { + Optional user = userRepository.findByEmailAndIsDeleted(query.getEmail(),true); + return user.map(userMapper::toDomainEntity); + } + @Override public UserImage loadUserImageByUserId(FindByUserIdQuery query) { UserImageJpaEntity userImageJpaEntity = @@ -307,4 +319,14 @@ public List loadUserImagesByUserIdIn(FindUserImagesByIdsQuery query) .map(userImageMapper::toDomainEntity) .toList(); } + + @Override + public Boolean checksNotDeletedUserByEmail(FindByEmailQuery query) { + return userRepository.existsByEmailAndIsDeleted(query.getEmail(), false); + } + + @Override + public Boolean checksUserSoftDeletedByEmail(FindByEmailQuery query) { + return userRepository.existsByEmailAndIsDeleted(query.getEmail(), true); + } } diff --git a/src/main/java/com/ftm/server/adapter/out/persistence/repository/EmailVerificationLogsRepository.java b/src/main/java/com/ftm/server/adapter/out/persistence/repository/EmailVerificationLogsRepository.java index ea40898..183a62f 100644 --- a/src/main/java/com/ftm/server/adapter/out/persistence/repository/EmailVerificationLogsRepository.java +++ b/src/main/java/com/ftm/server/adapter/out/persistence/repository/EmailVerificationLogsRepository.java @@ -3,6 +3,9 @@ import com.ftm.server.adapter.out.persistence.model.EmailVerificationLogsJpaEntity; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository @@ -16,4 +19,8 @@ Optional findByVerificationCodeAndEmail( Optional findByEmailAndIsVerified( String email, Boolean isVerified); + + @Modifying + @Query("DELETE FROM EmailVerificationLogsJpaEntity e WHERE e.email = :email") + void deleteAllByEmail(@Param("email") String email); } diff --git a/src/main/java/com/ftm/server/adapter/out/persistence/repository/UserRepository.java b/src/main/java/com/ftm/server/adapter/out/persistence/repository/UserRepository.java index 0597624..b2e90e1 100644 --- a/src/main/java/com/ftm/server/adapter/out/persistence/repository/UserRepository.java +++ b/src/main/java/com/ftm/server/adapter/out/persistence/repository/UserRepository.java @@ -15,6 +15,8 @@ public interface UserRepository extends JpaRepository { Optional findByIdAndIsDeleted(Long userId, Boolean isDeleted); + Optional findByEmailAndIsDeleted(String email, Boolean isDeleted); + Boolean existsByEmail(String email); Optional findByEmail(String email); @@ -33,4 +35,6 @@ Optional findBySocialProviderAndSocialId( @Query("SELECT u FROM UserJpaEntity u WHERE u.isDeleted = :isDeleted And u.deletedAt <=:end") List findAllByDeletedBefore( @Param("isDeleted") Boolean isDeleted, @Param("end") LocalDateTime end); + + Boolean existsByEmailAndIsDeleted(String email, boolean b); } diff --git a/src/main/java/com/ftm/server/application/command/user/DeleteByEmailCommand.java b/src/main/java/com/ftm/server/application/command/user/DeleteByEmailCommand.java new file mode 100644 index 0000000..a1ddbe4 --- /dev/null +++ b/src/main/java/com/ftm/server/application/command/user/DeleteByEmailCommand.java @@ -0,0 +1,12 @@ +package com.ftm.server.application.command.user; + +import lombok.Data; + +@Data +public class DeleteByEmailCommand { + private final String email; + + public static DeleteByEmailCommand of(String email) { + return new DeleteByEmailCommand(email); + } +} \ No newline at end of file diff --git a/src/main/java/com/ftm/server/application/command/user/DeleteUserByEmailCommand.java b/src/main/java/com/ftm/server/application/command/user/DeleteUserByEmailCommand.java new file mode 100644 index 0000000..3577947 --- /dev/null +++ b/src/main/java/com/ftm/server/application/command/user/DeleteUserByEmailCommand.java @@ -0,0 +1,13 @@ +package com.ftm.server.application.command.user; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class DeleteUserByEmailCommand { + private final String email; + public static DeleteUserByEmailCommand of(String email){ + return new DeleteUserByEmailCommand(email); + } +} diff --git a/src/main/java/com/ftm/server/application/port/in/user/UserHardDeleteByEmailUseCase.java b/src/main/java/com/ftm/server/application/port/in/user/UserHardDeleteByEmailUseCase.java new file mode 100644 index 0000000..db94977 --- /dev/null +++ b/src/main/java/com/ftm/server/application/port/in/user/UserHardDeleteByEmailUseCase.java @@ -0,0 +1,10 @@ +package com.ftm.server.application.port.in.user; + +import com.ftm.server.application.command.user.DeleteUserByEmailCommand; +import com.ftm.server.common.annotation.Port; +import org.springframework.security.core.parameters.P; + +@Port +public interface UserHardDeleteByEmailUseCase { + void execute(DeleteUserByEmailCommand command); +} diff --git a/src/main/java/com/ftm/server/application/port/out/persistence/user/CheckUserPort.java b/src/main/java/com/ftm/server/application/port/out/persistence/user/CheckUserPort.java index 1444af9..5d03fab 100644 --- a/src/main/java/com/ftm/server/application/port/out/persistence/user/CheckUserPort.java +++ b/src/main/java/com/ftm/server/application/port/out/persistence/user/CheckUserPort.java @@ -12,4 +12,8 @@ public interface CheckUserPort { Boolean checksUserBySocialValue(FindBySocialValueQuery query); Boolean checksUserById(FindByUserIdQuery query); + + Boolean checksNotDeletedUserByEmail(FindByEmailQuery query); + + Boolean checksUserSoftDeletedByEmail(FindByEmailQuery query); } diff --git a/src/main/java/com/ftm/server/application/port/out/persistence/user/DeleteEmailVerificationLogPort.java b/src/main/java/com/ftm/server/application/port/out/persistence/user/DeleteEmailVerificationLogPort.java new file mode 100644 index 0000000..3339459 --- /dev/null +++ b/src/main/java/com/ftm/server/application/port/out/persistence/user/DeleteEmailVerificationLogPort.java @@ -0,0 +1,9 @@ +package com.ftm.server.application.port.out.persistence.user; + +import com.ftm.server.application.command.user.DeleteByEmailCommand; +import com.ftm.server.common.annotation.Port; + +@Port +public interface DeleteEmailVerificationLogPort { + void deleteEmailVerificationLogsByEmail(DeleteByEmailCommand command); +} \ No newline at end of file diff --git a/src/main/java/com/ftm/server/application/port/out/persistence/user/LoadUserPort.java b/src/main/java/com/ftm/server/application/port/out/persistence/user/LoadUserPort.java index 012f4de..015bbd7 100644 --- a/src/main/java/com/ftm/server/application/port/out/persistence/user/LoadUserPort.java +++ b/src/main/java/com/ftm/server/application/port/out/persistence/user/LoadUserPort.java @@ -1,5 +1,6 @@ package com.ftm.server.application.port.out.persistence.user; +import com.ftm.server.application.query.FindByEmailQuery; import com.ftm.server.application.query.FindByUserIdQuery; import com.ftm.server.application.query.FindUserByDeleteOptionQuery; import com.ftm.server.application.query.FindUserByRoleQuery; @@ -14,4 +15,6 @@ public interface LoadUserPort { User loadUserByRole(FindUserByRoleQuery query); List loadUserByDeleteOption(FindUserByDeleteOptionQuery query); + + Optional loadDeletedUserByEmail(FindByEmailQuery query); } diff --git a/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService.java b/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService.java index ab0c6f9..464378e 100644 --- a/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService.java +++ b/src/main/java/com/ftm/server/application/service/user/EmailCodeVerificationService.java @@ -1,9 +1,11 @@ package com.ftm.server.application.service.user; import com.ftm.server.application.port.in.user.EmailCodeVerificationUseCase; +import com.ftm.server.application.port.out.persistence.user.CheckUserPort; import com.ftm.server.application.port.out.persistence.user.LoadEmailVerificationLogPort; import com.ftm.server.application.port.out.persistence.user.UpdateEmailVerificationLogPort; import com.ftm.server.application.query.EmailCodeVerificationQuery; +import com.ftm.server.application.query.FindByEmailQuery; import com.ftm.server.application.vo.user.EmailCodeVerificationVo; import com.ftm.server.domain.entity.EmailVerificationLogs; import jakarta.transaction.Transactional; @@ -17,6 +19,7 @@ public class EmailCodeVerificationService implements EmailCodeVerificationUseCas private final LoadEmailVerificationLogPort loadEmailVerificationLogPort; private final UpdateEmailVerificationLogPort updateEmailVerificationLogPort; + private final CheckUserPort checkUserPort; @Override @Transactional @@ -28,8 +31,13 @@ public EmailCodeVerificationVo execute(EmailCodeVerificationQuery query) { if (emailVerificationLogs.isEmpty()) { // 검증 코드가 일치하지 않음 return EmailCodeVerificationVo.of(false); } + emailVerificationLogs.get().updateVerificationStatus(true); // 검증 코드가 일치함 updateEmailVerificationLogPort.updateEmailVerificationLog(emailVerificationLogs.get()); - return EmailCodeVerificationVo.of(true); + + // 해당 이메일의 계정 상태 확인 (soft delete 여부) + Boolean isRecoverable = checkUserPort.checksUserSoftDeletedByEmail(FindByEmailQuery.of(query.getEmail())); + + return EmailCodeVerificationVo.of(true, isRecoverable); } } diff --git a/src/main/java/com/ftm/server/application/service/user/EmailDuplicationCheckService.java b/src/main/java/com/ftm/server/application/service/user/EmailDuplicationCheckService.java index ba96dc9..d1b7fd5 100644 --- a/src/main/java/com/ftm/server/application/service/user/EmailDuplicationCheckService.java +++ b/src/main/java/com/ftm/server/application/service/user/EmailDuplicationCheckService.java @@ -15,6 +15,6 @@ public class EmailDuplicationCheckService implements EmailDuplicationCheckUseCas @Override public EmailDuplicationVo execute(FindByEmailQuery query) { - return EmailDuplicationVo.of(checkUserPort.checksUserByEmail(query)); + return EmailDuplicationVo.of(checkUserPort.checksNotDeletedUserByEmail(query)); } } diff --git a/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService.java b/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService.java index a3f77ef..b8b3f83 100644 --- a/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService.java +++ b/src/main/java/com/ftm/server/application/service/user/GeneralUserSignupService.java @@ -1,10 +1,14 @@ package com.ftm.server.application.service.user; import com.ftm.server.adapter.in.web.user.dto.response.GeneralUserSignupResponse; +import com.ftm.server.application.command.user.DeleteByEmailCommand; +import com.ftm.server.application.command.user.DeleteUserByEmailCommand; import com.ftm.server.application.command.user.GeneralUserCreationCommand; import com.ftm.server.application.command.user.GeneralUserSignupCommand; import com.ftm.server.application.port.in.user.GeneralUserSignupUseCase; +import com.ftm.server.application.port.in.user.UserHardDeleteByEmailUseCase; import com.ftm.server.application.port.out.persistence.user.CheckUserPort; +import com.ftm.server.application.port.out.persistence.user.DeleteEmailVerificationLogPort; import com.ftm.server.application.port.out.persistence.user.LoadEmailVerificationLogPort; import com.ftm.server.application.port.out.persistence.user.SaveUserImagePort; import com.ftm.server.application.port.out.persistence.user.SaveUserPort; @@ -25,8 +29,12 @@ @RequiredArgsConstructor public class GeneralUserSignupService implements GeneralUserSignupUseCase { + //usecase + private final UserHardDeleteByEmailUseCase userHardDeleteByEmailUseCase; + // service private final LoadEmailVerificationLogPort loadEmailVerificationLogPort; + private final DeleteEmailVerificationLogPort deleteEmailVerificationLogPort; private final CheckUserPort checksUserPort; private final SaveUserPort saveUserPort; private final SaveUserImagePort saveUserImagePort; @@ -42,7 +50,7 @@ public GeneralUserSignupResponse execute(GeneralUserSignupCommand command) { loadEmailVerificationLogPort.loadEmailVerificationLogByEmail( FindByEmailQuery.of(email)); - if (checksUserPort.checksUserByEmail(FindByEmailQuery.of(email))) { // 기존에 가입된 회원인지 검사 + if (checksUserPort.checksNotDeletedUserByEmail(FindByEmailQuery.of(email))) { // 기존에 가입된 회원인지 검사(삭제되지 않은 user 에 한해서) throw new CustomException(ErrorResponseCode.USER_ALREADY_EXISTS); } @@ -50,6 +58,11 @@ public GeneralUserSignupResponse execute(GeneralUserSignupCommand command) { throw new CustomException(ErrorResponseCode.EMAIL_NOT_VERIFIED); } + //회원 탈퇴 후 복구 없이 재가입하는 경우, 기존의 계정 정보 즉시 삭제 + if(checksUserPort.checksUserSoftDeletedByEmail(FindByEmailQuery.of(email))){ + userHardDeleteByEmailUseCase.execute(DeleteUserByEmailCommand.of(email)); + } + String nickname = RandomNickNameCreator.generateNickname(); // random 닉네임 생성 GeneralUserCreationCommand convertedCommand = @@ -62,6 +75,10 @@ public GeneralUserSignupResponse execute(GeneralUserSignupCommand command) { User user = saveUserPort.saveUser(User.createGeneralUser(convertedCommand)); saveUserImagePort.saveUserDefaultImage(UserImage.createUserImage(user.getId())); + + // 회원가입 완료 후 해당 이메일의 인증 로그 삭제 + deleteEmailVerificationLogPort.deleteEmailVerificationLogsByEmail(DeleteByEmailCommand.of(email)); + return GeneralUserSignupResponse.of(user.getId()); } } diff --git a/src/main/java/com/ftm/server/application/service/user/UserHardDeleteByEmailService.java b/src/main/java/com/ftm/server/application/service/user/UserHardDeleteByEmailService.java new file mode 100644 index 0000000..a0fd31c --- /dev/null +++ b/src/main/java/com/ftm/server/application/service/user/UserHardDeleteByEmailService.java @@ -0,0 +1,60 @@ +package com.ftm.server.application.service.user; + +import com.ftm.server.application.command.user.*; +import com.ftm.server.application.port.in.user.UserHardDeleteByEmailUseCase; +import com.ftm.server.application.port.out.persistence.user.*; +import com.ftm.server.application.port.out.s3.S3ImageDeletePort; +import com.ftm.server.application.port.out.transcation.AfterCommitExecutorPort; +import com.ftm.server.application.query.FindByEmailQuery; +import com.ftm.server.application.query.FindUserByDeleteOptionQuery; +import com.ftm.server.domain.entity.User; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class UserHardDeleteByEmailService implements UserHardDeleteByEmailUseCase { + + private final LoadUserPort loadUserPort; + + private final DeleteBookmarkPort deleteBookmarkPort; + private final DeleteGroomingTestResultPort deleteGroomingTestResultPort; + private final DeleteUserImagePort deleteUserImagePort; + private final DeleteUserPort deleteUserPort; + + private final AfterCommitExecutorPort afterCommitExecutorPort; + private final S3ImageDeletePort s3ImageDeletePort; + + @Override + public void execute(DeleteUserByEmailCommand command) { + // 삭제 대상 user 조회 + Optional deletedUser = + loadUserPort.loadDeletedUserByEmail(FindByEmailQuery.of(command.getEmail())); + + if (deletedUser.isEmpty()){return;} + + List userId = List.of(deletedUser.get().getId()); + + // user 관련 엔티티 모두 삭제 + // 1. 북마크 삭제 + deleteBookmarkPort.deleteBookmarkByUserList( + DeleteBookmarkByUserIdCommand.of(userId)); + // 2. 그루밍 결과 삭제 + deleteGroomingTestResultPort.deleteGroomingTestResultByUserList( + DeleteGroomingTestResultByUserIdCommand.of(userId)); + // 3. user 이미지 삭제 + List imageKeyList = + deleteUserImagePort.deleteUserImageByUserList( + DeleteUserImageByUserIdCommand.of(userId)); + afterCommitExecutorPort.doAfterCommit( + () -> + s3ImageDeletePort.deleteImages( + imageKeyList)); // transaction commit 이후에 s3에 이미지 삭제 요청 + // 4. user 삭제 + deleteUserPort.deleteAllUserByIdList(DeleteAllUserByIdListCommand.of(userId)); + } +} diff --git a/src/main/java/com/ftm/server/application/vo/user/EmailCodeVerificationVo.java b/src/main/java/com/ftm/server/application/vo/user/EmailCodeVerificationVo.java index 022e946..d7988fd 100644 --- a/src/main/java/com/ftm/server/application/vo/user/EmailCodeVerificationVo.java +++ b/src/main/java/com/ftm/server/application/vo/user/EmailCodeVerificationVo.java @@ -5,8 +5,13 @@ @Data public class EmailCodeVerificationVo { private final Boolean isVerified; + private final Boolean isRecoverable; // 계정 복구 가능 여부 (soft delete 상태인지) public static EmailCodeVerificationVo of(Boolean isVerified) { - return new EmailCodeVerificationVo(isVerified); + return new EmailCodeVerificationVo(isVerified, false); + } + + public static EmailCodeVerificationVo of(Boolean isVerified, Boolean isRecoverable) { + return new EmailCodeVerificationVo(isVerified, isRecoverable); } } diff --git a/src/test/java/com/ftm/server/BaseTest.java b/src/test/java/com/ftm/server/BaseTest.java index 6e4bfc4..72b2ff2 100644 --- a/src/test/java/com/ftm/server/BaseTest.java +++ b/src/test/java/com/ftm/server/BaseTest.java @@ -16,6 +16,7 @@ import com.ftm.server.domain.enums.UserRole; import com.ftm.server.infrastructure.security.UserPrincipal; import groovy.util.logging.Slf4j; +import java.time.LocalDateTime; import java.util.List; import java.util.UUID; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/ftm/server/user/EmailCodeVerificationTest.java b/src/test/java/com/ftm/server/user/EmailCodeVerificationTest.java index d8a08a2..2bf4134 100644 --- a/src/test/java/com/ftm/server/user/EmailCodeVerificationTest.java +++ b/src/test/java/com/ftm/server/user/EmailCodeVerificationTest.java @@ -1,17 +1,28 @@ package com.ftm.server.user; import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static io.restassured.RestAssured.when; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.epages.restdocs.apispec.ResourceSnippetParameters; import com.ftm.server.BaseTest; import com.ftm.server.adapter.in.web.user.dto.request.EmailCodeVerificationRequest; +import com.ftm.server.adapter.in.web.user.dto.request.GeneralUserSignupRequest; import com.ftm.server.application.command.user.EmailVerificationLogCreationCommand; import com.ftm.server.application.port.out.persistence.user.SaveEmailVerificationLogPort; +import com.ftm.server.application.port.out.persistence.user.SaveUserPort; +import com.ftm.server.application.port.out.smtp.MailSenderPort; +import com.ftm.server.domain.entity.User; +import com.ftm.server.domain.enums.AgeGroup; +import com.ftm.server.domain.enums.HashTag; import jakarta.transaction.Transactional; +import java.time.LocalDateTime; import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -20,11 +31,16 @@ import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; import org.springframework.restdocs.payload.FieldDescriptor; import org.springframework.restdocs.payload.JsonFieldType; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.ResultActions; public class EmailCodeVerificationTest extends BaseTest { @Autowired private SaveEmailVerificationLogPort saveEmailVerificationLogPort; + @Autowired private SaveUserPort saveUserPort; + + @MockitoBean + private MailSenderPort mailSenderPort; private final List requestFieldDescriptors = List.of( @@ -39,7 +55,10 @@ public class EmailCodeVerificationTest extends BaseTest { fieldWithPath("data").type(JsonFieldType.OBJECT).optional().description("data"), fieldWithPath("data.isVerified") .type(JsonFieldType.BOOLEAN) - .description("검증 성공 여부")); + .description("검증 성공 여부"), + fieldWithPath("data.isRecoverable") + .type(JsonFieldType.BOOLEAN) + .description("계정 복구 가능 여부 : true 일 경우 계정 복구 요청 가능")); private ResultActions getResultActions(EmailCodeVerificationRequest request) throws Exception { return mockMvc.perform( // api 실행 @@ -66,6 +85,15 @@ private RestDocumentationResultHandler getDocument(Integer identifier) { .build())); } + protected User createSoftDeletedUser(String email, String password) { + User user = createTestUser(email, password); + user.updateIsDeleted(true); + user.updateDeletedAt(LocalDateTime.now()); + saveUserPort.saveUser(user); + return user; + } + + @Test @Transactional void 이메일_인증코드_검증_성공() throws Exception { @@ -75,6 +103,8 @@ private RestDocumentationResultHandler getDocument(Integer identifier) { // given saveEmailVerificationLogPort.saveEmailVerificationLogs( EmailVerificationLogCreationCommand.of(email, code)); + //stub 객체 생성 + doNothing().when(mailSenderPort).sendEmail(email,code); // when ResultActions resultActions =