From 3ce34103927a30f981c56846d133c31cc0d1330a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Sat, 13 Jan 2024 22:06:53 +0900 Subject: [PATCH 01/15] =?UTF-8?q?refactor:=20=ED=83=88=ED=87=B4=ED=95=9C?= =?UTF-8?q?=20=ED=9A=8C=EC=9B=90=20=EA=B2=80=EC=82=AC=20=EC=8A=A4=EC=BC=80?= =?UTF-8?q?=EC=A4=84=EB=A7=81=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/config/SchedulingConfig.java | 19 +++++++++++++++++++ .../server/user/service/UserService.java | 12 ++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 src/main/java/coffeemeet/server/common/config/SchedulingConfig.java diff --git a/src/main/java/coffeemeet/server/common/config/SchedulingConfig.java b/src/main/java/coffeemeet/server/common/config/SchedulingConfig.java new file mode 100644 index 00000000..3ce3ea72 --- /dev/null +++ b/src/main/java/coffeemeet/server/common/config/SchedulingConfig.java @@ -0,0 +1,19 @@ +package coffeemeet.server.common.config; + +import coffeemeet.server.user.service.UserService; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.Scheduled; + +@Configuration +@RequiredArgsConstructor +public class SchedulingConfig { + + private final UserService userService; + + @Scheduled(cron = "0 3 * * *") + public void checkUserDeleted() { + userService.deleteUserInfos(); + } + +} diff --git a/src/main/java/coffeemeet/server/user/service/UserService.java b/src/main/java/coffeemeet/server/user/service/UserService.java index d24bbdf7..786afcd1 100644 --- a/src/main/java/coffeemeet/server/user/service/UserService.java +++ b/src/main/java/coffeemeet/server/user/service/UserService.java @@ -122,6 +122,18 @@ public void deleteUser(Long userId) { userCommand.deleteUser(userId); } + public void deleteUserInfos() { + List users = userQuery.getUsersByDeleted(); + LocalDate today = LocalDate.now(); + + List deletedUsers = users.stream() + .filter(u -> u.getPrivacyDate() != null) + .filter(u -> u.getPrivacyDate().isBefore(today) || Objects.requireNonNull( + u.getPrivacyDate()).isEqual(today)) + .toList(); + deletedUsers.forEach(u -> userCommand.deleteUser(u.getId())); + } + public void registerOrUpdateNotificationToken(Long useId, String token) { userCommand.registerOrUpdateNotificationToken(useId, token); } From 45db551f76fec83509ba8df75cd0874a0ef7c6d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Sat, 13 Jan 2024 22:07:18 +0900 Subject: [PATCH 02/15] =?UTF-8?q?refactor:=20=ED=9A=8C=EC=9B=90=20?= =?UTF-8?q?=ED=83=88=ED=87=B4=20=EC=A0=95=EC=B1=85=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coffeemeet/server/user/domain/User.java | 10 +++++++++ .../server/user/implement/UserQuery.java | 4 ++++ .../user/infrastructure/UserRepository.java | 1 + .../server/user/service/UserService.java | 22 ++++++++++++++----- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/main/java/coffeemeet/server/user/domain/User.java b/src/main/java/coffeemeet/server/user/domain/User.java index ea2a4639..dfe0cecf 100644 --- a/src/main/java/coffeemeet/server/user/domain/User.java +++ b/src/main/java/coffeemeet/server/user/domain/User.java @@ -6,6 +6,7 @@ import static coffeemeet.server.user.domain.UserStatus.IDLE; import static coffeemeet.server.user.domain.UserStatus.MATCHING; import static coffeemeet.server.user.domain.UserStatus.REPORTED; +import static java.time.LocalDateTime.now; import coffeemeet.server.chatting.current.domain.ChattingRoom; import coffeemeet.server.common.domain.AdvancedBaseEntity; @@ -22,6 +23,7 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; +import java.time.LocalDate; import java.util.Objects; import lombok.AccessLevel; import lombok.Getter; @@ -65,6 +67,8 @@ public class User extends AdvancedBaseEntity { private boolean isDeleted; + private LocalDate privacyDate; + private boolean isBlacklisted; @Column(nullable = false) @@ -79,6 +83,7 @@ public void registerUser(@NonNull Profile profile) { this.reportInfo = new ReportInfo(); this.userStatus = IDLE; this.isDeleted = false; + this.privacyDate = null; this.isBlacklisted = false; this.isRegistered = true; } @@ -141,6 +146,11 @@ public void convertToBlacklist() { this.isBlacklisted = true; } + public void delete() { + this.isDeleted = true; + this.privacyDate = LocalDate.from(now().plusDays(30)); + } + @Override public final boolean equals(Object o) { if (this == o) { diff --git a/src/main/java/coffeemeet/server/user/implement/UserQuery.java b/src/main/java/coffeemeet/server/user/implement/UserQuery.java index e6d92527..ab7538a2 100644 --- a/src/main/java/coffeemeet/server/user/implement/UserQuery.java +++ b/src/main/java/coffeemeet/server/user/implement/UserQuery.java @@ -91,4 +91,8 @@ public List getUsersByRoom(ChattingRoom room) { return userRepository.findAllByChattingRoom(room); } + public List getUsersByDeleted() { + return userRepository.findAllByDeleted(true); + } + } diff --git a/src/main/java/coffeemeet/server/user/infrastructure/UserRepository.java b/src/main/java/coffeemeet/server/user/infrastructure/UserRepository.java index 9a715ca8..5a6a0a34 100644 --- a/src/main/java/coffeemeet/server/user/infrastructure/UserRepository.java +++ b/src/main/java/coffeemeet/server/user/infrastructure/UserRepository.java @@ -25,4 +25,5 @@ public interface UserRepository extends JpaRepository { List findAllByChattingRoom(ChattingRoom chattingRoom); + List findAllByDeleted(boolean isDeleted); } diff --git a/src/main/java/coffeemeet/server/user/service/UserService.java b/src/main/java/coffeemeet/server/user/service/UserService.java index 786afcd1..ed9ba80b 100644 --- a/src/main/java/coffeemeet/server/user/service/UserService.java +++ b/src/main/java/coffeemeet/server/user/service/UserService.java @@ -29,9 +29,11 @@ import coffeemeet.server.user.service.dto.UserProfileDto; import coffeemeet.server.user.service.dto.UserStatusDto; import java.io.File; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Collections; import java.util.List; +import java.util.Objects; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -67,17 +69,24 @@ public LoginDetailsDto login(OAuthProvider oAuthProvider, String authCode) { memberDetail.oAuthProviderId(), new Email(memberDetail.email()), memberDetail.profileImage()); User user = userQuery.getUserByOAuthInfoOrDefault(oauthInfo); + if (user.isRegistered()) { // TODO: 12/21/23 회원가입 중간에 나갈 때 예외 터지는 오류 잡기 - List interests = interestQuery.getKeywordsByUserId(user.getId()); - Certification certification = certificationQuery.getCertificationByUserId(user.getId()); - AuthTokens authTokens = authTokensGenerator.generate(user.getId()); - return LoginDetailsDto.of(user, interests, certification, authTokens); + if (!user.isDeleted() || hasStoredUserInfo(user)) { + List interests = interestQuery.getKeywordsByUserId(user.getId()); + Certification certification = certificationQuery.getCertificationByUserId(user.getId()); + AuthTokens authTokens = authTokensGenerator.generate(user.getId()); + return LoginDetailsDto.of(user, interests, certification, authTokens); + } } userCommand.saveUser(user); return LoginDetailsDto.of(user, Collections.emptyList(), null, null); } + private boolean hasStoredUserInfo(User user) { + return user.isDeleted() && user.getPrivacyDate() != null; + } + public UserProfileDto findUserProfile(Long userId) { User user = userQuery.getUserById(userId); List keywords = interestQuery.getKeywordsByUserId(userId); @@ -119,7 +128,10 @@ public void checkDuplicatedNickname(String nickname) { } public void deleteUser(Long userId) { - userCommand.deleteUser(userId); + User user = userQuery.getUserById(userId); + if (!user.isDeleted()) { + user.delete(); + } } public void deleteUserInfos() { From 4b77e6ac9eb3f665f6ce26b1938c21127c2005f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Tue, 16 Jan 2024 23:41:42 +0900 Subject: [PATCH 03/15] =?UTF-8?q?refactor:=20=EC=86=8C=EC=85=9C=20?= =?UTF-8?q?=ED=83=88=ED=87=B4=20=EB=A1=9C=EC=A7=81=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/auth/service/AuthService.java | 4 +- .../client/kakao/KakaoMemberClient.java | 4 +- .../client/naver/NaverMemberClient.java | 4 +- .../infrastructure/OAuthUnlinkClient.java | 11 ++++ .../OAuthUnlinkClientComposite.java | 39 +++++++++++++ .../infrastructure/OAuthUnlinkDetail.java | 9 +++ ...KakaoClient.java => KakaoFetchClient.java} | 3 +- .../kakao/KakaoUnlinkClient.java | 46 ++++++++++++++++ .../kakao/dto/KakaoUnlinkDetail.java | 11 ++++ ...NaverClient.java => NaverFetchClient.java} | 2 +- .../naver/NaverUnlinkClient.java | 55 +++++++++++++++++++ .../naver/dto/NaverUnlinkDetail.java | 14 +++++ .../oauth/presentation/OAuthController.java | 10 ++++ .../server/oauth/service/UnlinkService.java | 18 ++++++ .../server/user/service/UserService.java | 5 +- 15 files changed, 225 insertions(+), 10 deletions(-) create mode 100644 src/main/java/coffeemeet/server/oauth/infrastructure/OAuthUnlinkClient.java create mode 100644 src/main/java/coffeemeet/server/oauth/infrastructure/OAuthUnlinkClientComposite.java create mode 100644 src/main/java/coffeemeet/server/oauth/infrastructure/OAuthUnlinkDetail.java rename src/main/java/coffeemeet/server/oauth/infrastructure/kakao/{KakaoClient.java => KakaoFetchClient.java} (98%) create mode 100644 src/main/java/coffeemeet/server/oauth/infrastructure/kakao/KakaoUnlinkClient.java create mode 100644 src/main/java/coffeemeet/server/oauth/infrastructure/kakao/dto/KakaoUnlinkDetail.java rename src/main/java/coffeemeet/server/oauth/infrastructure/naver/{NaverClient.java => NaverFetchClient.java} (98%) create mode 100644 src/main/java/coffeemeet/server/oauth/infrastructure/naver/NaverUnlinkClient.java create mode 100644 src/main/java/coffeemeet/server/oauth/infrastructure/naver/dto/NaverUnlinkDetail.java create mode 100644 src/main/java/coffeemeet/server/oauth/service/UnlinkService.java diff --git a/src/main/java/coffeemeet/server/auth/service/AuthService.java b/src/main/java/coffeemeet/server/auth/service/AuthService.java index 151da26c..aeb3d3c5 100644 --- a/src/main/java/coffeemeet/server/auth/service/AuthService.java +++ b/src/main/java/coffeemeet/server/auth/service/AuthService.java @@ -38,8 +38,8 @@ public void logout(Long userId) { } @Transactional - public void delete(Long userId) { - userService.deleteUser(userId); + public void delete(Long userId, String accessToken) { + userService.deleteUser(userId, accessToken); refreshTokenCommand.deleteRefreshToken(userId); } diff --git a/src/main/java/coffeemeet/server/oauth/implement/client/kakao/KakaoMemberClient.java b/src/main/java/coffeemeet/server/oauth/implement/client/kakao/KakaoMemberClient.java index 1ee0a48a..7dad76dd 100644 --- a/src/main/java/coffeemeet/server/oauth/implement/client/kakao/KakaoMemberClient.java +++ b/src/main/java/coffeemeet/server/oauth/implement/client/kakao/KakaoMemberClient.java @@ -2,7 +2,7 @@ import coffeemeet.server.oauth.domain.OAuthMemberDetail; import coffeemeet.server.oauth.implement.client.OAuthMemberClient; -import coffeemeet.server.oauth.infrastructure.kakao.KakaoClient; +import coffeemeet.server.oauth.infrastructure.kakao.KakaoFetchClient; import coffeemeet.server.oauth.infrastructure.kakao.dto.KakaoMemberDetail; import coffeemeet.server.oauth.infrastructure.kakao.dto.KakaoTokens; import coffeemeet.server.user.domain.OAuthProvider; @@ -13,7 +13,7 @@ @RequiredArgsConstructor public class KakaoMemberClient implements OAuthMemberClient { - private final KakaoClient kakaoClient; + private final KakaoFetchClient kakaoClient; @Override public OAuthProvider oAuthProvider() { diff --git a/src/main/java/coffeemeet/server/oauth/implement/client/naver/NaverMemberClient.java b/src/main/java/coffeemeet/server/oauth/implement/client/naver/NaverMemberClient.java index f0cd5208..d5de4165 100644 --- a/src/main/java/coffeemeet/server/oauth/implement/client/naver/NaverMemberClient.java +++ b/src/main/java/coffeemeet/server/oauth/implement/client/naver/NaverMemberClient.java @@ -2,7 +2,7 @@ import coffeemeet.server.oauth.domain.OAuthMemberDetail; import coffeemeet.server.oauth.implement.client.OAuthMemberClient; -import coffeemeet.server.oauth.infrastructure.naver.NaverClient; +import coffeemeet.server.oauth.infrastructure.naver.NaverFetchClient; import coffeemeet.server.oauth.infrastructure.naver.dto.NaverMemberDetail; import coffeemeet.server.oauth.infrastructure.naver.dto.NaverTokens; import coffeemeet.server.user.domain.OAuthProvider; @@ -13,7 +13,7 @@ @RequiredArgsConstructor public class NaverMemberClient implements OAuthMemberClient { - private final NaverClient naverClient; + private final NaverFetchClient naverClient; @Override public OAuthProvider oAuthProvider() { diff --git a/src/main/java/coffeemeet/server/oauth/infrastructure/OAuthUnlinkClient.java b/src/main/java/coffeemeet/server/oauth/infrastructure/OAuthUnlinkClient.java new file mode 100644 index 00000000..17de32ac --- /dev/null +++ b/src/main/java/coffeemeet/server/oauth/infrastructure/OAuthUnlinkClient.java @@ -0,0 +1,11 @@ +package coffeemeet.server.oauth.infrastructure; + +import coffeemeet.server.user.domain.OAuthProvider; + +public interface OAuthUnlinkClient { + + OAuthProvider oAuthProvider(); + + OAuthUnlinkDetail unlink(String accessToken); + +} diff --git a/src/main/java/coffeemeet/server/oauth/infrastructure/OAuthUnlinkClientComposite.java b/src/main/java/coffeemeet/server/oauth/infrastructure/OAuthUnlinkClientComposite.java new file mode 100644 index 00000000..64755cc4 --- /dev/null +++ b/src/main/java/coffeemeet/server/oauth/infrastructure/OAuthUnlinkClientComposite.java @@ -0,0 +1,39 @@ +package coffeemeet.server.oauth.infrastructure; + +import static coffeemeet.server.auth.exception.AuthErrorCode.INVALID_LOGIN_TYPE; + +import coffeemeet.server.common.execption.InvalidAuthException; +import coffeemeet.server.user.domain.OAuthProvider; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import org.springframework.stereotype.Component; + +@Component +public class OAuthUnlinkClientComposite { + + private static final String INVALID_LOGIN_TYPE_MESSAGE = "로그인 타입(%s)에 일치하는 타입이 없습니다."; + + private final Map mapping; + + public OAuthUnlinkClientComposite(Set details) { + this.mapping = details.stream().collect( + Collectors.toUnmodifiableMap(OAuthUnlinkClient::oAuthProvider, Function.identity()) + ); + } + + public OAuthUnlinkDetail unlink(OAuthProvider oAuthProvider, String accessToken) { + return getClient(oAuthProvider).unlink(accessToken); + } + + private OAuthUnlinkClient getClient(OAuthProvider oAuthProvider) { + return Optional.ofNullable(mapping.get(oAuthProvider)) + .orElseThrow(() -> new InvalidAuthException( + INVALID_LOGIN_TYPE, + String.format(INVALID_LOGIN_TYPE_MESSAGE, oAuthProvider)) + ); + } + +} diff --git a/src/main/java/coffeemeet/server/oauth/infrastructure/OAuthUnlinkDetail.java b/src/main/java/coffeemeet/server/oauth/infrastructure/OAuthUnlinkDetail.java new file mode 100644 index 00000000..90f6f802 --- /dev/null +++ b/src/main/java/coffeemeet/server/oauth/infrastructure/OAuthUnlinkDetail.java @@ -0,0 +1,9 @@ +package coffeemeet.server.oauth.infrastructure; + +public record OAuthUnlinkDetail( + Long id, + String accessToken, + String result +) { + +} diff --git a/src/main/java/coffeemeet/server/oauth/infrastructure/kakao/KakaoClient.java b/src/main/java/coffeemeet/server/oauth/infrastructure/kakao/KakaoFetchClient.java similarity index 98% rename from src/main/java/coffeemeet/server/oauth/infrastructure/kakao/KakaoClient.java rename to src/main/java/coffeemeet/server/oauth/infrastructure/kakao/KakaoFetchClient.java index c34a91e6..942a6b61 100644 --- a/src/main/java/coffeemeet/server/oauth/infrastructure/kakao/KakaoClient.java +++ b/src/main/java/coffeemeet/server/oauth/infrastructure/kakao/KakaoFetchClient.java @@ -24,7 +24,7 @@ @Component @RequiredArgsConstructor -public class KakaoClient { +public class KakaoFetchClient { private static final String REQUEST_TOKEN_URL = "https://kauth.kakao.com/oauth/token"; private static final String REQUEST_INFO_URL = "https://kapi.kakao.com/v2/user/me"; @@ -58,7 +58,6 @@ public KakaoMemberDetail fetchMember(String accessToken) { httpHeaders.set(AUTHORIZATION, BEARER_TYPE + accessToken); HttpEntity request = new HttpEntity<>(httpHeaders); - KakaoMemberDetail response = restTemplate.exchange(REQUEST_INFO_URL, HttpMethod.GET, request, KakaoMemberDetail.class).getBody(); diff --git a/src/main/java/coffeemeet/server/oauth/infrastructure/kakao/KakaoUnlinkClient.java b/src/main/java/coffeemeet/server/oauth/infrastructure/kakao/KakaoUnlinkClient.java new file mode 100644 index 00000000..44f63cd8 --- /dev/null +++ b/src/main/java/coffeemeet/server/oauth/infrastructure/kakao/KakaoUnlinkClient.java @@ -0,0 +1,46 @@ +package coffeemeet.server.oauth.infrastructure.kakao; + +import static coffeemeet.server.oauth.utils.constant.OAuthConstant.AUTHORIZATION; +import static coffeemeet.server.oauth.utils.constant.OAuthConstant.BEARER_TYPE; + +import coffeemeet.server.oauth.infrastructure.OAuthUnlinkClient; +import coffeemeet.server.oauth.infrastructure.OAuthUnlinkDetail; +import coffeemeet.server.oauth.infrastructure.kakao.dto.KakaoUnlinkDetail; +import coffeemeet.server.user.domain.OAuthProvider; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; + +import org.springframework.http.HttpMethod; +import org.springframework.web.client.RestTemplate; + +@Component +@RequiredArgsConstructor +public class KakaoUnlinkClient implements OAuthUnlinkClient { + + private static final String UNLINK_USER_URL = "https://kapi.kakao.com/v1/user/unlink"; + + private final RestTemplate restTemplate; + + @Override + public OAuthProvider oAuthProvider() { + return OAuthProvider.KAKAO; + } + + public OAuthUnlinkDetail unlink(String accessToken) { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + httpHeaders.set(AUTHORIZATION, BEARER_TYPE + accessToken); + + HttpEntity request = new HttpEntity<>(httpHeaders); + + KakaoUnlinkDetail response = restTemplate.exchange(UNLINK_USER_URL, HttpMethod.POST, request, + KakaoUnlinkDetail.class).getBody(); + + assert response != null; + return response.toOAuthUnlinkDetail(); + } + +} diff --git a/src/main/java/coffeemeet/server/oauth/infrastructure/kakao/dto/KakaoUnlinkDetail.java b/src/main/java/coffeemeet/server/oauth/infrastructure/kakao/dto/KakaoUnlinkDetail.java new file mode 100644 index 00000000..e1713d3f --- /dev/null +++ b/src/main/java/coffeemeet/server/oauth/infrastructure/kakao/dto/KakaoUnlinkDetail.java @@ -0,0 +1,11 @@ +package coffeemeet.server.oauth.infrastructure.kakao.dto; + +import coffeemeet.server.oauth.infrastructure.OAuthUnlinkDetail; + +public record KakaoUnlinkDetail(Long userId) { + + public OAuthUnlinkDetail toOAuthUnlinkDetail() { + return new OAuthUnlinkDetail(this.userId(), null, null); + } + +} diff --git a/src/main/java/coffeemeet/server/oauth/infrastructure/naver/NaverClient.java b/src/main/java/coffeemeet/server/oauth/infrastructure/naver/NaverFetchClient.java similarity index 98% rename from src/main/java/coffeemeet/server/oauth/infrastructure/naver/NaverClient.java rename to src/main/java/coffeemeet/server/oauth/infrastructure/naver/NaverFetchClient.java index 4a0dc9a6..3b434977 100644 --- a/src/main/java/coffeemeet/server/oauth/infrastructure/naver/NaverClient.java +++ b/src/main/java/coffeemeet/server/oauth/infrastructure/naver/NaverFetchClient.java @@ -27,7 +27,7 @@ @Component @RequiredArgsConstructor -public class NaverClient { +public class NaverFetchClient { private static final String REQUEST_TOKEN_URL = "https://nid.naver.com/oauth2.0/token"; private static final String REQUEST_INFO_URL = "https://openapi.naver.com/v1/nid/me"; diff --git a/src/main/java/coffeemeet/server/oauth/infrastructure/naver/NaverUnlinkClient.java b/src/main/java/coffeemeet/server/oauth/infrastructure/naver/NaverUnlinkClient.java new file mode 100644 index 00000000..0e84e791 --- /dev/null +++ b/src/main/java/coffeemeet/server/oauth/infrastructure/naver/NaverUnlinkClient.java @@ -0,0 +1,55 @@ +package coffeemeet.server.oauth.infrastructure.naver; + +import static coffeemeet.server.oauth.utils.constant.OAuthConstant.AUTHORIZATION; +import static coffeemeet.server.oauth.utils.constant.OAuthConstant.AUTHORIZATION_CODE; +import static coffeemeet.server.oauth.utils.constant.OAuthConstant.BEARER_TYPE; +import static coffeemeet.server.oauth.utils.constant.OAuthConstant.CLIENT_ID; +import static coffeemeet.server.oauth.utils.constant.OAuthConstant.CLIENT_SECRET; +import static coffeemeet.server.oauth.utils.constant.OAuthConstant.GRANT_TYPE; + +import coffeemeet.server.oauth.config.naver.NaverProperties; +import coffeemeet.server.oauth.infrastructure.OAuthUnlinkClient; +import coffeemeet.server.oauth.infrastructure.OAuthUnlinkDetail; +import coffeemeet.server.oauth.infrastructure.naver.dto.NaverUnlinkDetail; +import coffeemeet.server.user.domain.OAuthProvider; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +@Component +@RequiredArgsConstructor +public class NaverUnlinkClient implements OAuthUnlinkClient { + + private static final String UNLINK_USER_URL = "https://nid.naver.com/oauth2.0/token"; + + private final RestTemplate restTemplate; + private final NaverProperties naverProperties; + + @Override + public OAuthProvider oAuthProvider() { + return OAuthProvider.NAVER; + } + + public OAuthUnlinkDetail unlink(String accessToken) { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + httpHeaders.set(AUTHORIZATION, BEARER_TYPE + accessToken); + httpHeaders.set(CLIENT_ID, naverProperties.getClientId()); + httpHeaders.set(CLIENT_SECRET, naverProperties.getClientSecret()); + httpHeaders.set(GRANT_TYPE, AUTHORIZATION_CODE); + + HttpEntity request = new HttpEntity<>(httpHeaders); + + NaverUnlinkDetail response = restTemplate.exchange(UNLINK_USER_URL, HttpMethod.POST, request, + NaverUnlinkDetail.class).getBody(); + + assert response != null; + return response.toOAuthUnlinkDetail(); + } + +} diff --git a/src/main/java/coffeemeet/server/oauth/infrastructure/naver/dto/NaverUnlinkDetail.java b/src/main/java/coffeemeet/server/oauth/infrastructure/naver/dto/NaverUnlinkDetail.java new file mode 100644 index 00000000..896ebc7b --- /dev/null +++ b/src/main/java/coffeemeet/server/oauth/infrastructure/naver/dto/NaverUnlinkDetail.java @@ -0,0 +1,14 @@ +package coffeemeet.server.oauth.infrastructure.naver.dto; + +import coffeemeet.server.oauth.infrastructure.OAuthUnlinkDetail; + +public record NaverUnlinkDetail( + String accessToken, + String result +) { + + public OAuthUnlinkDetail toOAuthUnlinkDetail() { + return new OAuthUnlinkDetail(null, this.accessToken, this.result); + } + +} diff --git a/src/main/java/coffeemeet/server/oauth/presentation/OAuthController.java b/src/main/java/coffeemeet/server/oauth/presentation/OAuthController.java index 86907a9e..85d6fa0d 100644 --- a/src/main/java/coffeemeet/server/oauth/presentation/OAuthController.java +++ b/src/main/java/coffeemeet/server/oauth/presentation/OAuthController.java @@ -1,6 +1,7 @@ package coffeemeet.server.oauth.presentation; import coffeemeet.server.oauth.service.OAuthService; +import coffeemeet.server.oauth.service.UnlinkService; import coffeemeet.server.user.domain.OAuthProvider; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @@ -9,6 +10,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -18,6 +20,7 @@ public class OAuthController { private final OAuthService oAuthService; + private final UnlinkService unlinkService; @GetMapping("/{oAuthProvider}") public ResponseEntity redirectAuthCodeRequestUrl(@PathVariable OAuthProvider oAuthProvider, @@ -27,4 +30,11 @@ public ResponseEntity redirectAuthCodeRequestUrl(@PathVariable OAuthProvid return new ResponseEntity<>(HttpStatus.FOUND); } + @PostMapping("/{oAuthProvider}/unlink") + public ResponseEntity unlink(@PathVariable OAuthProvider oAuthProvider, + String accessToken) { + unlinkService.unlink(oAuthProvider, accessToken); + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } diff --git a/src/main/java/coffeemeet/server/oauth/service/UnlinkService.java b/src/main/java/coffeemeet/server/oauth/service/UnlinkService.java new file mode 100644 index 00000000..613cfa42 --- /dev/null +++ b/src/main/java/coffeemeet/server/oauth/service/UnlinkService.java @@ -0,0 +1,18 @@ +package coffeemeet.server.oauth.service; + +import coffeemeet.server.oauth.infrastructure.OAuthUnlinkClientComposite; +import coffeemeet.server.user.domain.OAuthProvider; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class UnlinkService { + + private final OAuthUnlinkClientComposite oAuthUnlinkClientComposite; + + public void unlink(OAuthProvider oAuthProvider, String accessToken){ + oAuthUnlinkClientComposite.unlink(oAuthProvider, accessToken); + } + +} diff --git a/src/main/java/coffeemeet/server/user/service/UserService.java b/src/main/java/coffeemeet/server/user/service/UserService.java index ed9ba80b..0ee924d2 100644 --- a/src/main/java/coffeemeet/server/user/service/UserService.java +++ b/src/main/java/coffeemeet/server/user/service/UserService.java @@ -13,6 +13,7 @@ import coffeemeet.server.matching.implement.MatchingQueueCommand; import coffeemeet.server.oauth.domain.OAuthMemberDetail; import coffeemeet.server.oauth.implement.client.OAuthMemberClientComposite; +import coffeemeet.server.oauth.infrastructure.OAuthUnlinkClientComposite; import coffeemeet.server.user.domain.Email; import coffeemeet.server.user.domain.Keyword; import coffeemeet.server.user.domain.OAuthInfo; @@ -45,6 +46,7 @@ public class UserService { private static final String INVALID_REQUEST_MESSAGE = "사용자 상태에 맞지 않는 요청입니다."; private final ObjectStorage objectStorage; private final OAuthMemberClientComposite oAuthMemberClientComposite; + private final OAuthUnlinkClientComposite oAuthUnlinkClientComposite; private final CertificationQuery certificationQuery; private final AuthTokensGenerator authTokensGenerator; @@ -127,10 +129,11 @@ public void checkDuplicatedNickname(String nickname) { userQuery.hasDuplicatedNickname(nickname); } - public void deleteUser(Long userId) { + public void deleteUser(Long userId, String accessToken) { User user = userQuery.getUserById(userId); if (!user.isDeleted()) { user.delete(); + oAuthUnlinkClientComposite.unlink(user.getOauthInfo().getOauthProvider(), accessToken); } } From 8479672d775158e59c63db80a9a96260050e468a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Wed, 17 Jan 2024 00:11:23 +0900 Subject: [PATCH 04/15] =?UTF-8?q?refactor:=20=ED=83=88=ED=87=B4=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coffeemeet/server/auth/presentation/AuthController.java | 2 +- .../java/coffeemeet/server/user/service/UserService.java | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/coffeemeet/server/auth/presentation/AuthController.java b/src/main/java/coffeemeet/server/auth/presentation/AuthController.java index 4e394f93..95ec9354 100644 --- a/src/main/java/coffeemeet/server/auth/presentation/AuthController.java +++ b/src/main/java/coffeemeet/server/auth/presentation/AuthController.java @@ -30,7 +30,7 @@ public ResponseEntity logout(@Login AuthInfo authInfo) { @PostMapping("/delete") public ResponseEntity delete(@Login AuthInfo authInfo) { - authService.delete(authInfo.userId()); + authService.delete(authInfo.userId(), authInfo.refreshToken()); return ResponseEntity.ok().build(); } diff --git a/src/main/java/coffeemeet/server/user/service/UserService.java b/src/main/java/coffeemeet/server/user/service/UserService.java index 0ee924d2..6d47e804 100644 --- a/src/main/java/coffeemeet/server/user/service/UserService.java +++ b/src/main/java/coffeemeet/server/user/service/UserService.java @@ -44,6 +44,7 @@ public class UserService { private static final String INVALID_REQUEST_MESSAGE = "사용자 상태에 맞지 않는 요청입니다."; + private final ObjectStorage objectStorage; private final OAuthMemberClientComposite oAuthMemberClientComposite; private final OAuthUnlinkClientComposite oAuthUnlinkClientComposite; @@ -129,11 +130,11 @@ public void checkDuplicatedNickname(String nickname) { userQuery.hasDuplicatedNickname(nickname); } - public void deleteUser(Long userId, String accessToken) { + public void deleteUser(Long userId, String token) { User user = userQuery.getUserById(userId); if (!user.isDeleted()) { user.delete(); - oAuthUnlinkClientComposite.unlink(user.getOauthInfo().getOauthProvider(), accessToken); + oAuthUnlinkClientComposite.unlink(user.getOauthInfo().getOauthProvider(), token); } } From b43e373bbc286983fc29156df3d1668ef2d9cf94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Wed, 17 Jan 2024 11:37:49 +0900 Subject: [PATCH 05/15] =?UTF-8?q?refactor:=20=ED=83=88=ED=87=B4=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B0=8F=20=EC=9E=AC=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/presentation/AdminController.java | 1 + .../auth/presentation/AuthController.java | 5 +- .../server/auth/service/AuthService.java | 5 +- .../common/config/SchedulingConfig.java | 2 + .../oauth/presentation/OAuthController.java | 10 ---- .../server/oauth/service/UnlinkService.java | 18 ------- .../coffeemeet/server/user/domain/User.java | 14 ++++-- .../server/user/implement/UserQuery.java | 2 +- .../user/infrastructure/UserRepository.java | 3 +- .../server/user/service/UserService.java | 47 ++++++++++++------- 10 files changed, 50 insertions(+), 57 deletions(-) delete mode 100644 src/main/java/coffeemeet/server/oauth/service/UnlinkService.java diff --git a/src/main/java/coffeemeet/server/admin/presentation/AdminController.java b/src/main/java/coffeemeet/server/admin/presentation/AdminController.java index 502dd11f..9380173c 100644 --- a/src/main/java/coffeemeet/server/admin/presentation/AdminController.java +++ b/src/main/java/coffeemeet/server/admin/presentation/AdminController.java @@ -51,6 +51,7 @@ public class AdminController { private static final String REQUEST_WITHOUT_SESSION_MESSAGE = "SESSION 값이 존재하지 않습니다."; private static final String ADMIN_SESSION_ATTRIBUTE = "adminId"; + private final AdminService adminService; private final ReportService reportService; private final InquiryService inquiryService; diff --git a/src/main/java/coffeemeet/server/auth/presentation/AuthController.java b/src/main/java/coffeemeet/server/auth/presentation/AuthController.java index 95ec9354..cace3ccb 100644 --- a/src/main/java/coffeemeet/server/auth/presentation/AuthController.java +++ b/src/main/java/coffeemeet/server/auth/presentation/AuthController.java @@ -4,6 +4,7 @@ import coffeemeet.server.auth.service.AuthService; import coffeemeet.server.common.annotation.Login; import coffeemeet.server.common.domain.AuthInfo; +import coffeemeet.server.user.domain.OAuthProvider; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; @@ -29,8 +30,8 @@ public ResponseEntity logout(@Login AuthInfo authInfo) { } @PostMapping("/delete") - public ResponseEntity delete(@Login AuthInfo authInfo) { - authService.delete(authInfo.userId(), authInfo.refreshToken()); + public ResponseEntity delete(@Login AuthInfo authInfo, OAuthProvider oAuthProvider) { + authService.delete(authInfo.userId(), authInfo.refreshToken(), oAuthProvider); return ResponseEntity.ok().build(); } diff --git a/src/main/java/coffeemeet/server/auth/service/AuthService.java b/src/main/java/coffeemeet/server/auth/service/AuthService.java index aeb3d3c5..6a61ed68 100644 --- a/src/main/java/coffeemeet/server/auth/service/AuthService.java +++ b/src/main/java/coffeemeet/server/auth/service/AuthService.java @@ -7,6 +7,7 @@ import coffeemeet.server.auth.domain.JwtTokenProvider; import coffeemeet.server.auth.implement.RefreshTokenCommand; import coffeemeet.server.common.execption.InvalidAuthException; +import coffeemeet.server.user.domain.OAuthProvider; import coffeemeet.server.user.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -38,8 +39,8 @@ public void logout(Long userId) { } @Transactional - public void delete(Long userId, String accessToken) { - userService.deleteUser(userId, accessToken); + public void delete(Long userId, String token, OAuthProvider oAuthProvider) { + userService.deleteUser(userId, token, oAuthProvider); refreshTokenCommand.deleteRefreshToken(userId); } diff --git a/src/main/java/coffeemeet/server/common/config/SchedulingConfig.java b/src/main/java/coffeemeet/server/common/config/SchedulingConfig.java index 3ce3ea72..8ec73e18 100644 --- a/src/main/java/coffeemeet/server/common/config/SchedulingConfig.java +++ b/src/main/java/coffeemeet/server/common/config/SchedulingConfig.java @@ -3,9 +3,11 @@ import coffeemeet.server.user.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; @Configuration +@EnableScheduling @RequiredArgsConstructor public class SchedulingConfig { diff --git a/src/main/java/coffeemeet/server/oauth/presentation/OAuthController.java b/src/main/java/coffeemeet/server/oauth/presentation/OAuthController.java index 85d6fa0d..86907a9e 100644 --- a/src/main/java/coffeemeet/server/oauth/presentation/OAuthController.java +++ b/src/main/java/coffeemeet/server/oauth/presentation/OAuthController.java @@ -1,7 +1,6 @@ package coffeemeet.server.oauth.presentation; import coffeemeet.server.oauth.service.OAuthService; -import coffeemeet.server.oauth.service.UnlinkService; import coffeemeet.server.user.domain.OAuthProvider; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @@ -10,7 +9,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -20,7 +18,6 @@ public class OAuthController { private final OAuthService oAuthService; - private final UnlinkService unlinkService; @GetMapping("/{oAuthProvider}") public ResponseEntity redirectAuthCodeRequestUrl(@PathVariable OAuthProvider oAuthProvider, @@ -30,11 +27,4 @@ public ResponseEntity redirectAuthCodeRequestUrl(@PathVariable OAuthProvid return new ResponseEntity<>(HttpStatus.FOUND); } - @PostMapping("/{oAuthProvider}/unlink") - public ResponseEntity unlink(@PathVariable OAuthProvider oAuthProvider, - String accessToken) { - unlinkService.unlink(oAuthProvider, accessToken); - return new ResponseEntity<>(HttpStatus.NOT_FOUND); - } - } diff --git a/src/main/java/coffeemeet/server/oauth/service/UnlinkService.java b/src/main/java/coffeemeet/server/oauth/service/UnlinkService.java deleted file mode 100644 index 613cfa42..00000000 --- a/src/main/java/coffeemeet/server/oauth/service/UnlinkService.java +++ /dev/null @@ -1,18 +0,0 @@ -package coffeemeet.server.oauth.service; - -import coffeemeet.server.oauth.infrastructure.OAuthUnlinkClientComposite; -import coffeemeet.server.user.domain.OAuthProvider; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class UnlinkService { - - private final OAuthUnlinkClientComposite oAuthUnlinkClientComposite; - - public void unlink(OAuthProvider oAuthProvider, String accessToken){ - oAuthUnlinkClientComposite.unlink(oAuthProvider, accessToken); - } - -} diff --git a/src/main/java/coffeemeet/server/user/domain/User.java b/src/main/java/coffeemeet/server/user/domain/User.java index dfe0cecf..95c66543 100644 --- a/src/main/java/coffeemeet/server/user/domain/User.java +++ b/src/main/java/coffeemeet/server/user/domain/User.java @@ -6,7 +6,6 @@ import static coffeemeet.server.user.domain.UserStatus.IDLE; import static coffeemeet.server.user.domain.UserStatus.MATCHING; import static coffeemeet.server.user.domain.UserStatus.REPORTED; -import static java.time.LocalDateTime.now; import coffeemeet.server.chatting.current.domain.ChattingRoom; import coffeemeet.server.common.domain.AdvancedBaseEntity; @@ -23,7 +22,7 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; -import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.Objects; import lombok.AccessLevel; import lombok.Getter; @@ -67,7 +66,8 @@ public class User extends AdvancedBaseEntity { private boolean isDeleted; - private LocalDate privacyDate; + @Column(name = "privacy_date_time") + private LocalDateTime privacyDateTime; private boolean isBlacklisted; @@ -83,7 +83,7 @@ public void registerUser(@NonNull Profile profile) { this.reportInfo = new ReportInfo(); this.userStatus = IDLE; this.isDeleted = false; - this.privacyDate = null; + this.privacyDateTime = null; this.isBlacklisted = false; this.isRegistered = true; } @@ -148,7 +148,11 @@ public void convertToBlacklist() { public void delete() { this.isDeleted = true; - this.privacyDate = LocalDate.from(now().plusDays(30)); + this.privacyDateTime = LocalDateTime.now().plusDays(30); + } + + public void deletedWithdraw(){ + this.isDeleted = false; } @Override diff --git a/src/main/java/coffeemeet/server/user/implement/UserQuery.java b/src/main/java/coffeemeet/server/user/implement/UserQuery.java index ab7538a2..a3811aa1 100644 --- a/src/main/java/coffeemeet/server/user/implement/UserQuery.java +++ b/src/main/java/coffeemeet/server/user/implement/UserQuery.java @@ -92,7 +92,7 @@ public List getUsersByRoom(ChattingRoom room) { } public List getUsersByDeleted() { - return userRepository.findAllByDeleted(true); + return userRepository.findAllByIsDeletedIsTrue(); } } diff --git a/src/main/java/coffeemeet/server/user/infrastructure/UserRepository.java b/src/main/java/coffeemeet/server/user/infrastructure/UserRepository.java index 5a6a0a34..4d2aa55f 100644 --- a/src/main/java/coffeemeet/server/user/infrastructure/UserRepository.java +++ b/src/main/java/coffeemeet/server/user/infrastructure/UserRepository.java @@ -25,5 +25,6 @@ public interface UserRepository extends JpaRepository { List findAllByChattingRoom(ChattingRoom chattingRoom); - List findAllByDeleted(boolean isDeleted); + List findAllByIsDeletedIsTrue(); + } diff --git a/src/main/java/coffeemeet/server/user/service/UserService.java b/src/main/java/coffeemeet/server/user/service/UserService.java index 6d47e804..1f86186a 100644 --- a/src/main/java/coffeemeet/server/user/service/UserService.java +++ b/src/main/java/coffeemeet/server/user/service/UserService.java @@ -30,11 +30,9 @@ import coffeemeet.server.user.service.dto.UserProfileDto; import coffeemeet.server.user.service.dto.UserStatusDto; import java.io.File; -import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Collections; import java.util.List; -import java.util.Objects; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -75,21 +73,26 @@ public LoginDetailsDto login(OAuthProvider oAuthProvider, String authCode) { if (user.isRegistered()) { // TODO: 12/21/23 회원가입 중간에 나갈 때 예외 터지는 오류 잡기 - if (!user.isDeleted() || hasStoredUserInfo(user)) { - List interests = interestQuery.getKeywordsByUserId(user.getId()); - Certification certification = certificationQuery.getCertificationByUserId(user.getId()); - AuthTokens authTokens = authTokensGenerator.generate(user.getId()); - return LoginDetailsDto.of(user, interests, certification, authTokens); + if (user.isDeleted()) { + if (user.getPrivacyDateTime().isBefore(LocalDateTime.now())) { + user.deletedWithdraw(); + userCommand.updateUser(user); + List interests = interestQuery.getKeywordsByUserId(user.getId()); + Certification certification = certificationQuery.getCertificationByUserId(user.getId()); + AuthTokens authTokens = authTokensGenerator.generate(user.getId()); + return LoginDetailsDto.of(user, interests, certification, authTokens); + } else { + userCommand.deleteUser(user.getId()); + createUser(user); + } + } else { + createUser(user); } } userCommand.saveUser(user); return LoginDetailsDto.of(user, Collections.emptyList(), null, null); } - private boolean hasStoredUserInfo(User user) { - return user.isDeleted() && user.getPrivacyDate() != null; - } - public UserProfileDto findUserProfile(Long userId) { User user = userQuery.getUserById(userId); List keywords = interestQuery.getKeywordsByUserId(userId); @@ -130,22 +133,22 @@ public void checkDuplicatedNickname(String nickname) { userQuery.hasDuplicatedNickname(nickname); } - public void deleteUser(Long userId, String token) { + public void deleteUser(Long userId, String token, OAuthProvider oAuthProvider) { User user = userQuery.getUserById(userId); if (!user.isDeleted()) { user.delete(); - oAuthUnlinkClientComposite.unlink(user.getOauthInfo().getOauthProvider(), token); + oAuthUnlinkClientComposite.unlink(oAuthProvider, token); } } public void deleteUserInfos() { List users = userQuery.getUsersByDeleted(); - LocalDate today = LocalDate.now(); + LocalDateTime today = LocalDateTime.now(); List deletedUsers = users.stream() - .filter(u -> u.getPrivacyDate() != null) - .filter(u -> u.getPrivacyDate().isBefore(today) || Objects.requireNonNull( - u.getPrivacyDate()).isEqual(today)) + .filter(u -> u.getPrivacyDateTime() != null) + .filter( + u -> u.getPrivacyDateTime().isBefore(today) || u.getPrivacyDateTime().isEqual(today)) .toList(); deletedUsers.forEach(u -> userCommand.deleteUser(u.getId())); } @@ -172,6 +175,13 @@ public UserStatusDto getUserStatus(Long userId) { }; } + private LoginDetailsDto createUser(User user){ + List interests = interestQuery.getKeywordsByUserId(user.getId()); + Certification certification = certificationQuery.getCertificationByUserId(user.getId()); + AuthTokens authTokens = authTokensGenerator.generate(user.getId()); + return LoginDetailsDto.of(user, interests, certification, authTokens); + } + private UserStatusDto handleIdleUser(Certification certification) { boolean isCertificated = certification != null; return UserStatusDto.of(UserStatus.IDLE, null, null, null, isCertificated, null); @@ -186,7 +196,8 @@ private UserStatusDto handleMatchingUser(Long userId, Certification certificatio private UserStatusDto handleChattingUser(User user) { Long chattingRoomId = user.getChattingRoom().getId(); String chattingRoomName = user.getChattingRoom().getName(); - return UserStatusDto.of(UserStatus.CHATTING_UNCONNECTED, null, chattingRoomId, chattingRoomName, + return UserStatusDto.of(UserStatus.CHATTING_UNCONNECTED, null, chattingRoomId, + chattingRoomName, null, null); } From c478db6181d7504c9427a476f396ebc97f545415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Wed, 17 Jan 2024 12:23:50 +0900 Subject: [PATCH 06/15] =?UTF-8?q?style:=20OAuth=20=EB=93=B1=EB=A1=9D/?= =?UTF-8?q?=ED=83=88=ED=87=B4=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=AA=85=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...Composite.java => OAuthMemberClientRegistry.java} | 5 +++-- .../client/OAuthMemberUnlinkRegistry.java} | 7 ++++--- .../client}/OAuthUnlinkClient.java | 3 ++- .../infrastructure/kakao/KakaoUnlinkClient.java | 2 +- .../infrastructure/naver/NaverUnlinkClient.java | 2 +- .../coffeemeet/server/user/service/UserService.java | 12 ++++++------ 6 files changed, 17 insertions(+), 14 deletions(-) rename src/main/java/coffeemeet/server/oauth/implement/client/{OAuthMemberClientComposite.java => OAuthMemberClientRegistry.java} (91%) rename src/main/java/coffeemeet/server/oauth/{infrastructure/OAuthUnlinkClientComposite.java => implement/client/OAuthMemberUnlinkRegistry.java} (84%) rename src/main/java/coffeemeet/server/oauth/{infrastructure => implement/client}/OAuthUnlinkClient.java (60%) diff --git a/src/main/java/coffeemeet/server/oauth/implement/client/OAuthMemberClientComposite.java b/src/main/java/coffeemeet/server/oauth/implement/client/OAuthMemberClientRegistry.java similarity index 91% rename from src/main/java/coffeemeet/server/oauth/implement/client/OAuthMemberClientComposite.java rename to src/main/java/coffeemeet/server/oauth/implement/client/OAuthMemberClientRegistry.java index ece1393e..62553c46 100644 --- a/src/main/java/coffeemeet/server/oauth/implement/client/OAuthMemberClientComposite.java +++ b/src/main/java/coffeemeet/server/oauth/implement/client/OAuthMemberClientRegistry.java @@ -13,12 +13,13 @@ import org.springframework.stereotype.Component; @Component -public class OAuthMemberClientComposite { +public class OAuthMemberClientRegistry { private static final String INVALID_LOGIN_TYPE_MESSAGE = "로그인 타입(%s)에 일치하는 타입이 없습니다."; + private final Map mapping; - public OAuthMemberClientComposite(Set clients) { + public OAuthMemberClientRegistry(Set clients) { this.mapping = clients.stream().collect( Collectors.toUnmodifiableMap(OAuthMemberClient::oAuthProvider, Function.identity()) ); diff --git a/src/main/java/coffeemeet/server/oauth/infrastructure/OAuthUnlinkClientComposite.java b/src/main/java/coffeemeet/server/oauth/implement/client/OAuthMemberUnlinkRegistry.java similarity index 84% rename from src/main/java/coffeemeet/server/oauth/infrastructure/OAuthUnlinkClientComposite.java rename to src/main/java/coffeemeet/server/oauth/implement/client/OAuthMemberUnlinkRegistry.java index 64755cc4..b7c62325 100644 --- a/src/main/java/coffeemeet/server/oauth/infrastructure/OAuthUnlinkClientComposite.java +++ b/src/main/java/coffeemeet/server/oauth/implement/client/OAuthMemberUnlinkRegistry.java @@ -1,8 +1,9 @@ -package coffeemeet.server.oauth.infrastructure; +package coffeemeet.server.oauth.implement.client; import static coffeemeet.server.auth.exception.AuthErrorCode.INVALID_LOGIN_TYPE; import coffeemeet.server.common.execption.InvalidAuthException; +import coffeemeet.server.oauth.infrastructure.OAuthUnlinkDetail; import coffeemeet.server.user.domain.OAuthProvider; import java.util.Map; import java.util.Optional; @@ -12,13 +13,13 @@ import org.springframework.stereotype.Component; @Component -public class OAuthUnlinkClientComposite { +public class OAuthMemberUnlinkRegistry { private static final String INVALID_LOGIN_TYPE_MESSAGE = "로그인 타입(%s)에 일치하는 타입이 없습니다."; private final Map mapping; - public OAuthUnlinkClientComposite(Set details) { + public OAuthMemberUnlinkRegistry(Set details) { this.mapping = details.stream().collect( Collectors.toUnmodifiableMap(OAuthUnlinkClient::oAuthProvider, Function.identity()) ); diff --git a/src/main/java/coffeemeet/server/oauth/infrastructure/OAuthUnlinkClient.java b/src/main/java/coffeemeet/server/oauth/implement/client/OAuthUnlinkClient.java similarity index 60% rename from src/main/java/coffeemeet/server/oauth/infrastructure/OAuthUnlinkClient.java rename to src/main/java/coffeemeet/server/oauth/implement/client/OAuthUnlinkClient.java index 17de32ac..2608af54 100644 --- a/src/main/java/coffeemeet/server/oauth/infrastructure/OAuthUnlinkClient.java +++ b/src/main/java/coffeemeet/server/oauth/implement/client/OAuthUnlinkClient.java @@ -1,5 +1,6 @@ -package coffeemeet.server.oauth.infrastructure; +package coffeemeet.server.oauth.implement.client; +import coffeemeet.server.oauth.infrastructure.OAuthUnlinkDetail; import coffeemeet.server.user.domain.OAuthProvider; public interface OAuthUnlinkClient { diff --git a/src/main/java/coffeemeet/server/oauth/infrastructure/kakao/KakaoUnlinkClient.java b/src/main/java/coffeemeet/server/oauth/infrastructure/kakao/KakaoUnlinkClient.java index 44f63cd8..30b6f10d 100644 --- a/src/main/java/coffeemeet/server/oauth/infrastructure/kakao/KakaoUnlinkClient.java +++ b/src/main/java/coffeemeet/server/oauth/infrastructure/kakao/KakaoUnlinkClient.java @@ -3,7 +3,7 @@ import static coffeemeet.server.oauth.utils.constant.OAuthConstant.AUTHORIZATION; import static coffeemeet.server.oauth.utils.constant.OAuthConstant.BEARER_TYPE; -import coffeemeet.server.oauth.infrastructure.OAuthUnlinkClient; +import coffeemeet.server.oauth.implement.client.OAuthUnlinkClient; import coffeemeet.server.oauth.infrastructure.OAuthUnlinkDetail; import coffeemeet.server.oauth.infrastructure.kakao.dto.KakaoUnlinkDetail; import coffeemeet.server.user.domain.OAuthProvider; diff --git a/src/main/java/coffeemeet/server/oauth/infrastructure/naver/NaverUnlinkClient.java b/src/main/java/coffeemeet/server/oauth/infrastructure/naver/NaverUnlinkClient.java index 0e84e791..7a2f7827 100644 --- a/src/main/java/coffeemeet/server/oauth/infrastructure/naver/NaverUnlinkClient.java +++ b/src/main/java/coffeemeet/server/oauth/infrastructure/naver/NaverUnlinkClient.java @@ -8,7 +8,7 @@ import static coffeemeet.server.oauth.utils.constant.OAuthConstant.GRANT_TYPE; import coffeemeet.server.oauth.config.naver.NaverProperties; -import coffeemeet.server.oauth.infrastructure.OAuthUnlinkClient; +import coffeemeet.server.oauth.implement.client.OAuthUnlinkClient; import coffeemeet.server.oauth.infrastructure.OAuthUnlinkDetail; import coffeemeet.server.oauth.infrastructure.naver.dto.NaverUnlinkDetail; import coffeemeet.server.user.domain.OAuthProvider; diff --git a/src/main/java/coffeemeet/server/user/service/UserService.java b/src/main/java/coffeemeet/server/user/service/UserService.java index 1f86186a..7376e5e8 100644 --- a/src/main/java/coffeemeet/server/user/service/UserService.java +++ b/src/main/java/coffeemeet/server/user/service/UserService.java @@ -12,8 +12,8 @@ import coffeemeet.server.common.execption.BadRequestException; import coffeemeet.server.matching.implement.MatchingQueueCommand; import coffeemeet.server.oauth.domain.OAuthMemberDetail; -import coffeemeet.server.oauth.implement.client.OAuthMemberClientComposite; -import coffeemeet.server.oauth.infrastructure.OAuthUnlinkClientComposite; +import coffeemeet.server.oauth.implement.client.OAuthMemberClientRegistry; +import coffeemeet.server.oauth.implement.client.OAuthMemberUnlinkRegistry; import coffeemeet.server.user.domain.Email; import coffeemeet.server.user.domain.Keyword; import coffeemeet.server.user.domain.OAuthInfo; @@ -44,8 +44,8 @@ public class UserService { private static final String INVALID_REQUEST_MESSAGE = "사용자 상태에 맞지 않는 요청입니다."; private final ObjectStorage objectStorage; - private final OAuthMemberClientComposite oAuthMemberClientComposite; - private final OAuthUnlinkClientComposite oAuthUnlinkClientComposite; + private final OAuthMemberClientRegistry oAuthMemberClientRegistry; + private final OAuthMemberUnlinkRegistry oAuthMemberUnlinkRegistry; private final CertificationQuery certificationQuery; private final AuthTokensGenerator authTokensGenerator; @@ -65,7 +65,7 @@ public void signup(Long userId, String nickname, List keywords) { } public LoginDetailsDto login(OAuthProvider oAuthProvider, String authCode) { - OAuthMemberDetail memberDetail = oAuthMemberClientComposite.fetch(oAuthProvider, authCode); + OAuthMemberDetail memberDetail = oAuthMemberClientRegistry.fetch(oAuthProvider, authCode); OAuthInfo oauthInfo = new OAuthInfo(memberDetail.oAuthProvider(), memberDetail.oAuthProviderId(), new Email(memberDetail.email()), memberDetail.profileImage()); @@ -137,7 +137,7 @@ public void deleteUser(Long userId, String token, OAuthProvider oAuthProvider) { User user = userQuery.getUserById(userId); if (!user.isDeleted()) { user.delete(); - oAuthUnlinkClientComposite.unlink(oAuthProvider, token); + oAuthMemberUnlinkRegistry.unlink(oAuthProvider, token); } } From 67f21326bb8ee382d9f2886749a7a8c301644ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Wed, 17 Jan 2024 12:27:14 +0900 Subject: [PATCH 07/15] =?UTF-8?q?refactor:=20=ED=83=88=ED=87=B4=20?= =?UTF-8?q?=ED=9B=84=20=EC=9E=AC=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coffeemeet/server/user/service/UserService.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/main/java/coffeemeet/server/user/service/UserService.java b/src/main/java/coffeemeet/server/user/service/UserService.java index 7376e5e8..9f2ecc09 100644 --- a/src/main/java/coffeemeet/server/user/service/UserService.java +++ b/src/main/java/coffeemeet/server/user/service/UserService.java @@ -83,10 +83,12 @@ public LoginDetailsDto login(OAuthProvider oAuthProvider, String authCode) { return LoginDetailsDto.of(user, interests, certification, authTokens); } else { userCommand.deleteUser(user.getId()); - createUser(user); } } else { - createUser(user); + List interests = interestQuery.getKeywordsByUserId(user.getId()); + Certification certification = certificationQuery.getCertificationByUserId(user.getId()); + AuthTokens authTokens = authTokensGenerator.generate(user.getId()); + return LoginDetailsDto.of(user, interests, certification, authTokens); } } userCommand.saveUser(user); @@ -175,13 +177,6 @@ public UserStatusDto getUserStatus(Long userId) { }; } - private LoginDetailsDto createUser(User user){ - List interests = interestQuery.getKeywordsByUserId(user.getId()); - Certification certification = certificationQuery.getCertificationByUserId(user.getId()); - AuthTokens authTokens = authTokensGenerator.generate(user.getId()); - return LoginDetailsDto.of(user, interests, certification, authTokens); - } - private UserStatusDto handleIdleUser(Certification certification) { boolean isCertificated = certification != null; return UserStatusDto.of(UserStatus.IDLE, null, null, null, isCertificated, null); From 3701f6143e5c1e93f196eb660779db139506f50c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Wed, 17 Jan 2024 12:28:31 +0900 Subject: [PATCH 08/15] =?UTF-8?q?style:=20=EC=BB=A8=EB=B2=A4=EC=85=98=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/presentation/resolver/UserArgumentResolver.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/coffeemeet/server/common/presentation/resolver/UserArgumentResolver.java b/src/main/java/coffeemeet/server/common/presentation/resolver/UserArgumentResolver.java index 445a9c92..ba883442 100644 --- a/src/main/java/coffeemeet/server/common/presentation/resolver/UserArgumentResolver.java +++ b/src/main/java/coffeemeet/server/common/presentation/resolver/UserArgumentResolver.java @@ -23,6 +23,7 @@ public class UserArgumentResolver implements HandlerMethodArgumentResolver { private static final int AUTHENTICATION_PREFIX_LENGTH = 7; private static final String HEADER_AUTHENTICATION_FAILED_MESSAGE = "(%s)는 잘못된 권한 헤더입니다."; + private final JwtTokenProvider jwtTokenProvider; private final RefreshTokenQuery refreshTokenQuery; From fd95006800c54d365516a55bf8d4402fe003db6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Wed, 17 Jan 2024 12:34:55 +0900 Subject: [PATCH 09/15] =?UTF-8?q?refactor:=20=EC=8A=A4=EC=BC=80=EC=A5=B4?= =?UTF-8?q?=EB=9F=AC=20=EC=84=A4=EC=A0=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/coffeemeet/server/ServerApplication.java | 2 ++ .../server/common/config/SchedulingConfig.java | 11 ++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/coffeemeet/server/ServerApplication.java b/src/main/java/coffeemeet/server/ServerApplication.java index ec48a22b..ae042a7f 100644 --- a/src/main/java/coffeemeet/server/ServerApplication.java +++ b/src/main/java/coffeemeet/server/ServerApplication.java @@ -2,8 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication +@EnableScheduling public class ServerApplication { public static void main(String[] args) { diff --git a/src/main/java/coffeemeet/server/common/config/SchedulingConfig.java b/src/main/java/coffeemeet/server/common/config/SchedulingConfig.java index 8ec73e18..7edf868b 100644 --- a/src/main/java/coffeemeet/server/common/config/SchedulingConfig.java +++ b/src/main/java/coffeemeet/server/common/config/SchedulingConfig.java @@ -2,19 +2,20 @@ import coffeemeet.server.user.service.UserService; import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Configuration; -import org.springframework.scheduling.annotation.EnableScheduling; +import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; -@Configuration -@EnableScheduling +@Slf4j +@Component @RequiredArgsConstructor public class SchedulingConfig { private final UserService userService; - @Scheduled(cron = "0 3 * * *") + @Scheduled(cron = "0 0 3 * * *") public void checkUserDeleted() { + log.info("탈퇴 후 30일이 지난 회원 스케줄링 처리"); userService.deleteUserInfos(); } From 6dcf7800d3ff9ea1eddbd185f4f2f71b409d17a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Sun, 21 Jan 2024 12:55:28 +0900 Subject: [PATCH 10/15] =?UTF-8?q?refactor:=20=ED=9A=8C=EC=9B=90=20?= =?UTF-8?q?=ED=83=88=ED=87=B4=20=EB=A1=9C=EC=A7=81=20OAuthService=EB=A1=9C?= =?UTF-8?q?=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/presentation/AuthController.java | 7 ------- .../server/auth/service/AuthService.java | 10 ---------- ... => AuthCodeRequestUrlProviderRegistry.java} | 4 ++-- .../oauth/presentation/OAuthController.java | 9 +++++++++ .../server/oauth/service/OAuthService.java | 17 ++++++++++++++--- 5 files changed, 25 insertions(+), 22 deletions(-) rename src/main/java/coffeemeet/server/oauth/implement/provider/{AuthCodeRequestUrlProviderComposite.java => AuthCodeRequestUrlProviderRegistry.java} (90%) diff --git a/src/main/java/coffeemeet/server/auth/presentation/AuthController.java b/src/main/java/coffeemeet/server/auth/presentation/AuthController.java index cace3ccb..cf359a26 100644 --- a/src/main/java/coffeemeet/server/auth/presentation/AuthController.java +++ b/src/main/java/coffeemeet/server/auth/presentation/AuthController.java @@ -4,7 +4,6 @@ import coffeemeet.server.auth.service.AuthService; import coffeemeet.server.common.annotation.Login; import coffeemeet.server.common.domain.AuthInfo; -import coffeemeet.server.user.domain.OAuthProvider; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; @@ -29,10 +28,4 @@ public ResponseEntity logout(@Login AuthInfo authInfo) { return ResponseEntity.ok().build(); } - @PostMapping("/delete") - public ResponseEntity delete(@Login AuthInfo authInfo, OAuthProvider oAuthProvider) { - authService.delete(authInfo.userId(), authInfo.refreshToken(), oAuthProvider); - return ResponseEntity.ok().build(); - } - } diff --git a/src/main/java/coffeemeet/server/auth/service/AuthService.java b/src/main/java/coffeemeet/server/auth/service/AuthService.java index 6a61ed68..fb692a3a 100644 --- a/src/main/java/coffeemeet/server/auth/service/AuthService.java +++ b/src/main/java/coffeemeet/server/auth/service/AuthService.java @@ -7,11 +7,8 @@ import coffeemeet.server.auth.domain.JwtTokenProvider; import coffeemeet.server.auth.implement.RefreshTokenCommand; import coffeemeet.server.common.execption.InvalidAuthException; -import coffeemeet.server.user.domain.OAuthProvider; -import coffeemeet.server.user.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor @@ -19,7 +16,6 @@ public class AuthService { private static final String EXPIRED_REFRESH_TOKEN_MESSAGE = "리프레시 토큰(%s)이 만료되었습니다. 다시 로그인해 주세요."; - private final UserService userService; private final AuthTokensGenerator authTokensGenerator; private final JwtTokenProvider jwtTokenProvider; private final RefreshTokenCommand refreshTokenCommand; @@ -38,10 +34,4 @@ public void logout(Long userId) { refreshTokenCommand.deleteRefreshToken(userId); } - @Transactional - public void delete(Long userId, String token, OAuthProvider oAuthProvider) { - userService.deleteUser(userId, token, oAuthProvider); - refreshTokenCommand.deleteRefreshToken(userId); - } - } diff --git a/src/main/java/coffeemeet/server/oauth/implement/provider/AuthCodeRequestUrlProviderComposite.java b/src/main/java/coffeemeet/server/oauth/implement/provider/AuthCodeRequestUrlProviderRegistry.java similarity index 90% rename from src/main/java/coffeemeet/server/oauth/implement/provider/AuthCodeRequestUrlProviderComposite.java rename to src/main/java/coffeemeet/server/oauth/implement/provider/AuthCodeRequestUrlProviderRegistry.java index b65f9722..89ee0d37 100644 --- a/src/main/java/coffeemeet/server/oauth/implement/provider/AuthCodeRequestUrlProviderComposite.java +++ b/src/main/java/coffeemeet/server/oauth/implement/provider/AuthCodeRequestUrlProviderRegistry.java @@ -12,12 +12,12 @@ import org.springframework.stereotype.Component; @Component -public class AuthCodeRequestUrlProviderComposite { +public class AuthCodeRequestUrlProviderRegistry { private static final String INVALID_LOGIN_TYPE_MESSAGE = "로그인 타입(%s)에 일치하는 타입이 없습니다."; private final Map mapping; - public AuthCodeRequestUrlProviderComposite(Set providers) { + public AuthCodeRequestUrlProviderRegistry(Set providers) { this.mapping = providers.stream().collect( Collectors.toUnmodifiableMap( AuthCodeRequestUrlProvider::oAuthProvider, diff --git a/src/main/java/coffeemeet/server/oauth/presentation/OAuthController.java b/src/main/java/coffeemeet/server/oauth/presentation/OAuthController.java index 86907a9e..0191eb08 100644 --- a/src/main/java/coffeemeet/server/oauth/presentation/OAuthController.java +++ b/src/main/java/coffeemeet/server/oauth/presentation/OAuthController.java @@ -1,5 +1,7 @@ package coffeemeet.server.oauth.presentation; +import coffeemeet.server.common.annotation.Login; +import coffeemeet.server.common.domain.AuthInfo; import coffeemeet.server.oauth.service.OAuthService; import coffeemeet.server.user.domain.OAuthProvider; import jakarta.servlet.http.HttpServletResponse; @@ -9,6 +11,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -27,4 +30,10 @@ public ResponseEntity redirectAuthCodeRequestUrl(@PathVariable OAuthProvid return new ResponseEntity<>(HttpStatus.FOUND); } + @PostMapping("/delete") + public ResponseEntity delete(@Login AuthInfo authInfo, OAuthProvider oAuthProvider) { + oAuthService.delete(authInfo.userId(), authInfo.refreshToken(), oAuthProvider); + return ResponseEntity.ok().build(); + } + } diff --git a/src/main/java/coffeemeet/server/oauth/service/OAuthService.java b/src/main/java/coffeemeet/server/oauth/service/OAuthService.java index 21cd2d33..e1b2d962 100644 --- a/src/main/java/coffeemeet/server/oauth/service/OAuthService.java +++ b/src/main/java/coffeemeet/server/oauth/service/OAuthService.java @@ -1,7 +1,10 @@ package coffeemeet.server.oauth.service; -import coffeemeet.server.oauth.implement.provider.AuthCodeRequestUrlProviderComposite; +import coffeemeet.server.auth.implement.RefreshTokenCommand; +import coffeemeet.server.oauth.implement.provider.AuthCodeRequestUrlProviderRegistry; import coffeemeet.server.user.domain.OAuthProvider; +import coffeemeet.server.user.service.UserService; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -9,10 +12,18 @@ @RequiredArgsConstructor public class OAuthService { - private final AuthCodeRequestUrlProviderComposite authCodeRequestUrlProviderComposite; + private final AuthCodeRequestUrlProviderRegistry authCodeRequestUrlProviderRegistry; + private final UserService userService; + private final RefreshTokenCommand refreshTokenCommand; public String getAuthCodeRequestUrl(OAuthProvider oAuthProvider) { - return authCodeRequestUrlProviderComposite.provide(oAuthProvider); + return authCodeRequestUrlProviderRegistry.provide(oAuthProvider); + } + + @Transactional + public void delete(Long userId, String token, OAuthProvider oAuthProvider) { + userService.deleteUser(userId, token, oAuthProvider); + refreshTokenCommand.deleteRefreshToken(userId); } } From fae7176412664228541d15cac62ef180d135a922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Sun, 21 Jan 2024 12:56:27 +0900 Subject: [PATCH 11/15] =?UTF-8?q?refactor:=20=EC=9C=A0=EC=A0=80=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=95=84=20=EA=B4=80=EB=A0=A8=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/presentation/UserController.java | 10 ++- .../user/service/UserProfileService.java | 81 +++++++++++++++++++ .../server/user/service/UserService.java | 54 ------------- 3 files changed, 87 insertions(+), 58 deletions(-) create mode 100644 src/main/java/coffeemeet/server/user/service/UserProfileService.java diff --git a/src/main/java/coffeemeet/server/user/presentation/UserController.java b/src/main/java/coffeemeet/server/user/presentation/UserController.java index f43c6a16..7ecba82b 100644 --- a/src/main/java/coffeemeet/server/user/presentation/UserController.java +++ b/src/main/java/coffeemeet/server/user/presentation/UserController.java @@ -12,6 +12,7 @@ import coffeemeet.server.user.presentation.dto.UpdateProfileHTTP; import coffeemeet.server.user.presentation.dto.UserProfileHTTP; import coffeemeet.server.user.presentation.dto.UserStatusHTTP; +import coffeemeet.server.user.service.UserProfileService; import coffeemeet.server.user.service.UserService; import coffeemeet.server.user.service.dto.LoginDetailsDto; import coffeemeet.server.user.service.dto.MyProfileDto; @@ -43,6 +44,7 @@ public class UserController { private final UserService userService; + private final UserProfileService userProfileService; @PostMapping("/sign-up") public ResponseEntity signup(@Valid @RequestBody SignupHTTP.Request request) { @@ -59,13 +61,13 @@ public ResponseEntity login(@PathVariable OAuthProvid @GetMapping("/{userId}") public ResponseEntity getUserProfile(@PathVariable Long userId) { - UserProfileDto response = userService.findUserProfile(userId); + UserProfileDto response = userProfileService.findUserProfile(userId); return ResponseEntity.ok(UserProfileHTTP.Response.of(response)); } @GetMapping("/me") public ResponseEntity getMyProfile(@Login AuthInfo authInfo) { - MyProfileDto response = userService.findMyProfile(authInfo.userId()); + MyProfileDto response = userProfileService.findMyProfile(authInfo.userId()); return ResponseEntity.ok(MyProfileHTTP.Response.of(response)); } @@ -82,7 +84,7 @@ public ResponseEntity updateProfileImage( @Login AuthInfo authInfo, @RequestPart("profileImage") @NotNull MultipartFile profileImage) { - userService.updateProfileImage( + userProfileService.updateProfileImage( authInfo.userId(), FileUtils.convertMultipartFileToFile(profileImage)); return ResponseEntity.ok().build(); @@ -91,7 +93,7 @@ public ResponseEntity updateProfileImage( @PatchMapping("/me") public ResponseEntity updateProfileInfo(@Login AuthInfo authInfo, @Valid @RequestBody UpdateProfileHTTP.Request request) { - userService.updateProfileInfo(authInfo.userId(), request.nickname(), request.interests()); + userProfileService.updateProfileInfo(authInfo.userId(), request.nickname(), request.interests()); return ResponseEntity.ok().build(); } diff --git a/src/main/java/coffeemeet/server/user/service/UserProfileService.java b/src/main/java/coffeemeet/server/user/service/UserProfileService.java new file mode 100644 index 00000000..703efa3d --- /dev/null +++ b/src/main/java/coffeemeet/server/user/service/UserProfileService.java @@ -0,0 +1,81 @@ +package coffeemeet.server.user.service; + +import static coffeemeet.server.common.domain.S3KeyPrefix.PROFILE_IMAGE; +import static coffeemeet.server.oauth.utils.constant.OAuthConstant.DEFAULT_IMAGE_URL; + +import coffeemeet.server.certification.domain.Certification; +import coffeemeet.server.certification.implement.CertificationQuery; +import coffeemeet.server.common.infrastructure.S3ObjectStorage; +import coffeemeet.server.user.domain.Keyword; +import coffeemeet.server.user.domain.User; +import coffeemeet.server.user.implement.InterestCommand; +import coffeemeet.server.user.implement.InterestQuery; +import coffeemeet.server.user.implement.UserCommand; +import coffeemeet.server.user.implement.UserQuery; +import coffeemeet.server.user.service.dto.MyProfileDto; +import coffeemeet.server.user.service.dto.UserProfileDto; +import jakarta.transaction.Transactional; +import java.io.File; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class UserProfileService { + + private final UserQuery userQuery; + private final InterestQuery interestQuery; + private final CertificationQuery certificationQuery; + private final S3ObjectStorage objectStorage; + private final UserCommand userCommand; + private final InterestCommand interestCommand; + + public UserProfileDto findUserProfile(Long userId) { + User user = userQuery.getUserById(userId); + List keywords = interestQuery.getKeywordsByUserId(userId); + Certification certification = certificationQuery.getCertificationByUserId(userId); + return UserProfileDto.of(user, keywords, certification); + } + + public MyProfileDto findMyProfile(Long userId) { + User user = userQuery.getUserById(userId); + List keywords = interestQuery.getKeywordsByUserId(userId); + Certification certification = certificationQuery.getCertificationByUserId(userId); + return MyProfileDto.of(user, keywords, certification); + } + + public void updateProfileImage(Long userId, File file) { + User user = userQuery.getUserById(userId); + deleteCurrentProfileImage(user.getOauthInfo().getProfileImageUrl()); + + String key = objectStorage.generateKey(PROFILE_IMAGE); + objectStorage.upload(key, file); + user.updateProfileImageUrl(objectStorage.getUrl(key)); + userCommand.updateUser(user); + } + + @Transactional + public void updateProfileInfo(Long userId, String nickname, + List keywords) { + User user = userQuery.getUserById(userId); + if (nickname != null) { + userCommand.updateUserInfo(user, nickname); + } + if (keywords != null && !keywords.isEmpty()) { + interestCommand.updateInterests(user, keywords); + } + } + + private void deleteCurrentProfileImage(String profileImageUrl) { + if (!profileImageUrl.equals(DEFAULT_IMAGE_URL)) { + String currentKey = objectStorage.extractKey(profileImageUrl, + PROFILE_IMAGE); + if (currentKey.isBlank()) { + return; + } + objectStorage.delete(currentKey); + } + } + +} diff --git a/src/main/java/coffeemeet/server/user/service/UserService.java b/src/main/java/coffeemeet/server/user/service/UserService.java index 9f2ecc09..cebe60da 100644 --- a/src/main/java/coffeemeet/server/user/service/UserService.java +++ b/src/main/java/coffeemeet/server/user/service/UserService.java @@ -1,14 +1,11 @@ package coffeemeet.server.user.service; -import static coffeemeet.server.common.domain.S3KeyPrefix.PROFILE_IMAGE; import static coffeemeet.server.common.execption.GlobalErrorCode.BAD_REQUEST_ERROR; -import static coffeemeet.server.oauth.utils.constant.OAuthConstant.DEFAULT_IMAGE_URL; import coffeemeet.server.auth.domain.AuthTokens; import coffeemeet.server.auth.domain.AuthTokensGenerator; import coffeemeet.server.certification.domain.Certification; import coffeemeet.server.certification.implement.CertificationQuery; -import coffeemeet.server.common.domain.ObjectStorage; import coffeemeet.server.common.execption.BadRequestException; import coffeemeet.server.matching.implement.MatchingQueueCommand; import coffeemeet.server.oauth.domain.OAuthMemberDetail; @@ -26,10 +23,7 @@ import coffeemeet.server.user.implement.UserCommand; import coffeemeet.server.user.implement.UserQuery; import coffeemeet.server.user.service.dto.LoginDetailsDto; -import coffeemeet.server.user.service.dto.MyProfileDto; -import coffeemeet.server.user.service.dto.UserProfileDto; import coffeemeet.server.user.service.dto.UserStatusDto; -import java.io.File; import java.time.LocalDateTime; import java.util.Collections; import java.util.List; @@ -43,7 +37,6 @@ public class UserService { private static final String INVALID_REQUEST_MESSAGE = "사용자 상태에 맞지 않는 요청입니다."; - private final ObjectStorage objectStorage; private final OAuthMemberClientRegistry oAuthMemberClientRegistry; private final OAuthMemberUnlinkRegistry oAuthMemberUnlinkRegistry; @@ -95,42 +88,6 @@ public LoginDetailsDto login(OAuthProvider oAuthProvider, String authCode) { return LoginDetailsDto.of(user, Collections.emptyList(), null, null); } - public UserProfileDto findUserProfile(Long userId) { - User user = userQuery.getUserById(userId); - List keywords = interestQuery.getKeywordsByUserId(userId); - Certification certification = certificationQuery.getCertificationByUserId(userId); - return UserProfileDto.of(user, keywords, certification); - } - - public MyProfileDto findMyProfile(Long userId) { - User user = userQuery.getUserById(userId); - List keywords = interestQuery.getKeywordsByUserId(userId); - Certification certification = certificationQuery.getCertificationByUserId(userId); - return MyProfileDto.of(user, keywords, certification); - } - - public void updateProfileImage(Long userId, File file) { - User user = userQuery.getUserById(userId); - deleteCurrentProfileImage(user.getOauthInfo().getProfileImageUrl()); - - String key = objectStorage.generateKey(PROFILE_IMAGE); - objectStorage.upload(key, file); - user.updateProfileImageUrl(objectStorage.getUrl(key)); - userCommand.updateUser(user); - } - - @Transactional - public void updateProfileInfo(Long userId, String nickname, - List keywords) { - User user = userQuery.getUserById(userId); - if (nickname != null) { - userCommand.updateUserInfo(user, nickname); - } - if (keywords != null && !keywords.isEmpty()) { - interestCommand.updateInterests(user, keywords); - } - } - public void checkDuplicatedNickname(String nickname) { userQuery.hasDuplicatedNickname(nickname); } @@ -201,15 +158,4 @@ private UserStatusDto handleReportedUser(User user) { return UserStatusDto.of(UserStatus.REPORTED, null, null, null, null, penaltyExpiration); } - private void deleteCurrentProfileImage(String profileImageUrl) { - if (!profileImageUrl.equals(DEFAULT_IMAGE_URL)) { - String currentKey = objectStorage.extractKey(profileImageUrl, - PROFILE_IMAGE); - if (currentKey.isBlank()) { - return; - } - objectStorage.delete(currentKey); - } - } - } From e4cc0215fd9522699024d81221845a5505199be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Sun, 21 Jan 2024 13:47:19 +0900 Subject: [PATCH 12/15] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=ED=9A=8C=EC=9B=90=20?= =?UTF-8?q?=ED=83=88=ED=87=B4=20=EB=82=A0=EC=A7=9C=20=EB=B9=84=EA=B5=90=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/common/config/SchedulingConfig.java | 2 +- .../server/oauth/presentation/OAuthController.java | 4 ++-- .../server/oauth/service/OAuthService.java | 4 ++-- .../coffeemeet/server/user/implement/UserQuery.java | 2 +- .../coffeemeet/server/user/service/UserService.java | 12 +++++------- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/main/java/coffeemeet/server/common/config/SchedulingConfig.java b/src/main/java/coffeemeet/server/common/config/SchedulingConfig.java index 7edf868b..dd5b2344 100644 --- a/src/main/java/coffeemeet/server/common/config/SchedulingConfig.java +++ b/src/main/java/coffeemeet/server/common/config/SchedulingConfig.java @@ -15,7 +15,7 @@ public class SchedulingConfig { @Scheduled(cron = "0 0 3 * * *") public void checkUserDeleted() { - log.info("탈퇴 후 30일이 지난 회원 스케줄링 처리"); + log.info("탈퇴 후 30일이 지난 회원 정보 제거"); userService.deleteUserInfos(); } diff --git a/src/main/java/coffeemeet/server/oauth/presentation/OAuthController.java b/src/main/java/coffeemeet/server/oauth/presentation/OAuthController.java index 0191eb08..cc2d9edb 100644 --- a/src/main/java/coffeemeet/server/oauth/presentation/OAuthController.java +++ b/src/main/java/coffeemeet/server/oauth/presentation/OAuthController.java @@ -31,8 +31,8 @@ public ResponseEntity redirectAuthCodeRequestUrl(@PathVariable OAuthProvid } @PostMapping("/delete") - public ResponseEntity delete(@Login AuthInfo authInfo, OAuthProvider oAuthProvider) { - oAuthService.delete(authInfo.userId(), authInfo.refreshToken(), oAuthProvider); + public ResponseEntity unlink(@Login AuthInfo authInfo, OAuthProvider oAuthProvider) { + oAuthService.unlink(authInfo.userId(), authInfo.refreshToken(), oAuthProvider); return ResponseEntity.ok().build(); } diff --git a/src/main/java/coffeemeet/server/oauth/service/OAuthService.java b/src/main/java/coffeemeet/server/oauth/service/OAuthService.java index e1b2d962..c0a37a3e 100644 --- a/src/main/java/coffeemeet/server/oauth/service/OAuthService.java +++ b/src/main/java/coffeemeet/server/oauth/service/OAuthService.java @@ -21,8 +21,8 @@ public String getAuthCodeRequestUrl(OAuthProvider oAuthProvider) { } @Transactional - public void delete(Long userId, String token, OAuthProvider oAuthProvider) { - userService.deleteUser(userId, token, oAuthProvider); + public void unlink(Long userId, String accessToken, OAuthProvider oAuthProvider) { + userService.deleteUser(userId, accessToken, oAuthProvider); refreshTokenCommand.deleteRefreshToken(userId); } diff --git a/src/main/java/coffeemeet/server/user/implement/UserQuery.java b/src/main/java/coffeemeet/server/user/implement/UserQuery.java index a3811aa1..9fb67499 100644 --- a/src/main/java/coffeemeet/server/user/implement/UserQuery.java +++ b/src/main/java/coffeemeet/server/user/implement/UserQuery.java @@ -91,7 +91,7 @@ public List getUsersByRoom(ChattingRoom room) { return userRepository.findAllByChattingRoom(room); } - public List getUsersByDeleted() { + public List getDeletedUsers() { return userRepository.findAllByIsDeletedIsTrue(); } diff --git a/src/main/java/coffeemeet/server/user/service/UserService.java b/src/main/java/coffeemeet/server/user/service/UserService.java index cebe60da..1246d7ff 100644 --- a/src/main/java/coffeemeet/server/user/service/UserService.java +++ b/src/main/java/coffeemeet/server/user/service/UserService.java @@ -92,22 +92,20 @@ public void checkDuplicatedNickname(String nickname) { userQuery.hasDuplicatedNickname(nickname); } - public void deleteUser(Long userId, String token, OAuthProvider oAuthProvider) { + public void deleteUser(Long userId, String accessToken, OAuthProvider oAuthProvider) { User user = userQuery.getUserById(userId); - if (!user.isDeleted()) { - user.delete(); - oAuthMemberUnlinkRegistry.unlink(oAuthProvider, token); - } + user.delete(); + oAuthMemberUnlinkRegistry.unlink(oAuthProvider, accessToken); } public void deleteUserInfos() { - List users = userQuery.getUsersByDeleted(); + List users = userQuery.getDeletedUsers(); LocalDateTime today = LocalDateTime.now(); List deletedUsers = users.stream() .filter(u -> u.getPrivacyDateTime() != null) .filter( - u -> u.getPrivacyDateTime().isBefore(today) || u.getPrivacyDateTime().isEqual(today)) + u -> u.getPrivacyDateTime().isAfter(today) || u.getPrivacyDateTime().isEqual(today)) .toList(); deletedUsers.forEach(u -> userCommand.deleteUser(u.getId())); } From 39b48335ebc4e4fb6d06a7bda85311b569aec089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Wed, 31 Jan 2024 20:11:38 +0900 Subject: [PATCH 13/15] =?UTF-8?q?test:=20=ED=9A=8C=EC=9B=90=20=ED=83=88?= =?UTF-8?q?=ED=87=B4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/presentation/AuthControllerTest.java | 25 --- .../server/auth/service/AuthServiceTest.java | 17 +- ...thCodeRequestUrlProviderRegistryTest.java} | 8 +- .../OAuthMemberClientCompositeTest.java | 8 +- .../client/kakao/KakaoMemberClientTest.java | 4 +- .../client/naver/NaverMemberClientTest.java | 4 +- .../infrastructure/kakao/KakaoClientTest.java | 2 +- .../infrastructure/naver/NaverClientTest.java | 2 +- .../presentation/OAuthControllerTest.java | 37 +++++ .../oauth/service/OAuthServiceTest.java | 10 +- .../user/presentation/UserControllerTest.java | 10 +- .../user/service/UserProfileServiceTest.java | 156 ++++++++++++++++++ .../server/user/service/UserServiceTest.java | 137 ++++----------- 13 files changed, 255 insertions(+), 165 deletions(-) rename src/test/java/coffeemeet/server/oauth/authcode/{AuthCodeRequestUrlProviderCompositeTest.java => AuthCodeRequestUrlProviderRegistryTest.java} (88%) create mode 100644 src/test/java/coffeemeet/server/user/service/UserProfileServiceTest.java diff --git a/src/test/java/coffeemeet/server/auth/presentation/AuthControllerTest.java b/src/test/java/coffeemeet/server/auth/presentation/AuthControllerTest.java index 1f7e531c..73c10f8e 100644 --- a/src/test/java/coffeemeet/server/auth/presentation/AuthControllerTest.java +++ b/src/test/java/coffeemeet/server/auth/presentation/AuthControllerTest.java @@ -94,29 +94,4 @@ void logoutTest() throws Exception { .andExpect(status().isOk()); } - @DisplayName("사용자는 회원탈퇴 할 수 있다.") - @Test - void deleteTest() throws Exception { - // given - RefreshToken refreshToken = AuthFixture.refreshToken(); - willDoNothing().given(authService).delete(anyLong()); - given(refreshTokenQuery.getRefreshToken(anyLong())).willReturn(refreshToken); - - // when, then - mockMvc.perform(post("/api/v1/auth/delete") - .header("Authorization", TOKEN) - .contentType(MediaType.APPLICATION_JSON) - ) - .andDo(document("auth-delete", - resourceDetails().tag("인증").description("회원탈퇴"), - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - requestHeaders( - headerWithName("Authorization").description("토큰") - ) - ) - ) - .andExpect(status().isOk()); - } - } diff --git a/src/test/java/coffeemeet/server/auth/service/AuthServiceTest.java b/src/test/java/coffeemeet/server/auth/service/AuthServiceTest.java index 14ba36c1..62e573a1 100644 --- a/src/test/java/coffeemeet/server/auth/service/AuthServiceTest.java +++ b/src/test/java/coffeemeet/server/auth/service/AuthServiceTest.java @@ -15,6 +15,7 @@ import coffeemeet.server.auth.implement.RefreshTokenCommand; import coffeemeet.server.common.execption.InvalidAuthException; import coffeemeet.server.common.fixture.AuthFixture; +import coffeemeet.server.oauth.service.OAuthService; import coffeemeet.server.user.service.UserService; import org.instancio.Instancio; import org.junit.jupiter.api.DisplayName; @@ -32,6 +33,9 @@ class AuthServiceTest { @InjectMocks private AuthService authService; + @InjectMocks + private OAuthService oAuthService; + @Mock private UserService userService; @@ -89,17 +93,4 @@ void logoutTest() { .doesNotThrowAnyException(); } - @DisplayName("회원 탈퇴 시킬 수 있다.") - @Test - void deleteTest() { - // given - Long userId = Instancio.create(Long.class); - willDoNothing().given(refreshTokenCommand).deleteRefreshToken(anyLong()); - willDoNothing().given(userService).deleteUser(anyLong()); - - // when, then - assertThatCode(() -> authService.delete(userId)) - .doesNotThrowAnyException(); - } - } diff --git a/src/test/java/coffeemeet/server/oauth/authcode/AuthCodeRequestUrlProviderCompositeTest.java b/src/test/java/coffeemeet/server/oauth/authcode/AuthCodeRequestUrlProviderRegistryTest.java similarity index 88% rename from src/test/java/coffeemeet/server/oauth/authcode/AuthCodeRequestUrlProviderCompositeTest.java rename to src/test/java/coffeemeet/server/oauth/authcode/AuthCodeRequestUrlProviderRegistryTest.java index 2ef7e037..b7af8f6f 100644 --- a/src/test/java/coffeemeet/server/oauth/authcode/AuthCodeRequestUrlProviderCompositeTest.java +++ b/src/test/java/coffeemeet/server/oauth/authcode/AuthCodeRequestUrlProviderRegistryTest.java @@ -4,7 +4,7 @@ import static org.mockito.BDDMockito.given; import coffeemeet.server.oauth.implement.provider.AuthCodeRequestUrlProvider; -import coffeemeet.server.oauth.implement.provider.AuthCodeRequestUrlProviderComposite; +import coffeemeet.server.oauth.implement.provider.AuthCodeRequestUrlProviderRegistry; import coffeemeet.server.user.domain.OAuthProvider; import java.util.Collections; import java.util.HashSet; @@ -17,7 +17,7 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -class AuthCodeRequestUrlProviderCompositeTest { +class AuthCodeRequestUrlProviderRegistryTest { @Mock private AuthCodeRequestUrlProvider provider; @@ -26,7 +26,7 @@ class AuthCodeRequestUrlProviderCompositeTest { private Set providers; @InjectMocks - private AuthCodeRequestUrlProviderComposite composite; + private AuthCodeRequestUrlProviderRegistry composite; @DisplayName("sns 의 redirect url 을 제공할 수 있다.") @Test @@ -39,7 +39,7 @@ void provideTest() { given(provider.provide()).willReturn(resultUrl); providers = new HashSet<>(Collections.singletonList(provider)); - composite = new AuthCodeRequestUrlProviderComposite(providers); + composite = new AuthCodeRequestUrlProviderRegistry(providers); // when String expectedUrl = composite.provide(oAuthProvider); diff --git a/src/test/java/coffeemeet/server/oauth/client/OAuthMemberClientCompositeTest.java b/src/test/java/coffeemeet/server/oauth/client/OAuthMemberClientCompositeTest.java index 9f1c2ac8..956093c1 100644 --- a/src/test/java/coffeemeet/server/oauth/client/OAuthMemberClientCompositeTest.java +++ b/src/test/java/coffeemeet/server/oauth/client/OAuthMemberClientCompositeTest.java @@ -6,7 +6,7 @@ import coffeemeet.server.common.fixture.OauthFixture; import coffeemeet.server.oauth.domain.OAuthMemberDetail; import coffeemeet.server.oauth.implement.client.OAuthMemberClient; -import coffeemeet.server.oauth.implement.client.OAuthMemberClientComposite; +import coffeemeet.server.oauth.implement.client.OAuthMemberClientRegistry; import coffeemeet.server.user.domain.OAuthProvider; import java.util.Collections; import java.util.HashSet; @@ -28,7 +28,7 @@ class OAuthMemberClientCompositeTest { private Set clients; @InjectMocks - private OAuthMemberClientComposite composite; + private OAuthMemberClientRegistry oAuthMemberClientRegistry; @DisplayName("sns 로부터 사용자 정보를 가져올 수 있다.") @Test @@ -42,10 +42,10 @@ void fetchTest() { given(client.fetch(authCode)).willReturn(response); clients = new HashSet<>(Collections.singletonList(client)); - composite = new OAuthMemberClientComposite(clients); + oAuthMemberClientRegistry = new OAuthMemberClientRegistry(clients); // when - OAuthMemberDetail expectedResponse = composite.fetch(oAuthProvider, authCode); + OAuthMemberDetail expectedResponse = oAuthMemberClientRegistry.fetch(oAuthProvider, authCode); // then assertThat(response).isEqualTo(expectedResponse); diff --git a/src/test/java/coffeemeet/server/oauth/implement/client/kakao/KakaoMemberClientTest.java b/src/test/java/coffeemeet/server/oauth/implement/client/kakao/KakaoMemberClientTest.java index 6bf403cb..4161cb45 100644 --- a/src/test/java/coffeemeet/server/oauth/implement/client/kakao/KakaoMemberClientTest.java +++ b/src/test/java/coffeemeet/server/oauth/implement/client/kakao/KakaoMemberClientTest.java @@ -5,7 +5,7 @@ import static org.mockito.BDDMockito.given; import coffeemeet.server.common.fixture.OauthFixture; -import coffeemeet.server.oauth.infrastructure.kakao.KakaoClient; +import coffeemeet.server.oauth.infrastructure.kakao.KakaoFetchClient; import coffeemeet.server.oauth.infrastructure.kakao.dto.KakaoMemberDetail; import coffeemeet.server.oauth.infrastructure.kakao.dto.KakaoTokens; import coffeemeet.server.user.domain.OAuthProvider; @@ -23,7 +23,7 @@ class KakaoMemberClientTest { KakaoMemberClient kakaoMemberClient; @Mock - KakaoClient kakaoClient; + KakaoFetchClient kakaoClient; @DisplayName("카카오 프로바이더를 가져올 수 있다.") @Test diff --git a/src/test/java/coffeemeet/server/oauth/implement/client/naver/NaverMemberClientTest.java b/src/test/java/coffeemeet/server/oauth/implement/client/naver/NaverMemberClientTest.java index 49f06f35..30734582 100644 --- a/src/test/java/coffeemeet/server/oauth/implement/client/naver/NaverMemberClientTest.java +++ b/src/test/java/coffeemeet/server/oauth/implement/client/naver/NaverMemberClientTest.java @@ -5,7 +5,7 @@ import static org.mockito.BDDMockito.given; import coffeemeet.server.common.fixture.OauthFixture; -import coffeemeet.server.oauth.infrastructure.naver.NaverClient; +import coffeemeet.server.oauth.infrastructure.naver.NaverFetchClient; import coffeemeet.server.oauth.infrastructure.naver.dto.NaverMemberDetail; import coffeemeet.server.oauth.infrastructure.naver.dto.NaverTokens; import coffeemeet.server.user.domain.OAuthProvider; @@ -23,7 +23,7 @@ class NaverMemberClientTest { private NaverMemberClient naverMemberClient; @Mock - private NaverClient naverClient; + private NaverFetchClient naverClient; @DisplayName("네이버 프로바이더를 가져올 수 있다.") @Test diff --git a/src/test/java/coffeemeet/server/oauth/infrastructure/kakao/KakaoClientTest.java b/src/test/java/coffeemeet/server/oauth/infrastructure/kakao/KakaoClientTest.java index b62043a6..d476a929 100644 --- a/src/test/java/coffeemeet/server/oauth/infrastructure/kakao/KakaoClientTest.java +++ b/src/test/java/coffeemeet/server/oauth/infrastructure/kakao/KakaoClientTest.java @@ -33,7 +33,7 @@ class KakaoClientTest { private KakaoProperties kakaoProperties; @InjectMocks - private KakaoClient kakaoClient; + private KakaoFetchClient kakaoClient; @DisplayName("카카오로부터 인증 토큰을 받을 수 있다.") @Test diff --git a/src/test/java/coffeemeet/server/oauth/infrastructure/naver/NaverClientTest.java b/src/test/java/coffeemeet/server/oauth/infrastructure/naver/NaverClientTest.java index 24cd622c..e0d6549e 100644 --- a/src/test/java/coffeemeet/server/oauth/infrastructure/naver/NaverClientTest.java +++ b/src/test/java/coffeemeet/server/oauth/infrastructure/naver/NaverClientTest.java @@ -34,7 +34,7 @@ class NaverClientTest { private NaverProperties naverProperties; @InjectMocks - private NaverClient naverClient; + private NaverFetchClient naverClient; @DisplayName("네이버로부터 인증 토큰을 받을 수 있다.") @Test diff --git a/src/test/java/coffeemeet/server/oauth/presentation/OAuthControllerTest.java b/src/test/java/coffeemeet/server/oauth/presentation/OAuthControllerTest.java index efae7862..1d47817d 100644 --- a/src/test/java/coffeemeet/server/oauth/presentation/OAuthControllerTest.java +++ b/src/test/java/coffeemeet/server/oauth/presentation/OAuthControllerTest.java @@ -2,10 +2,16 @@ import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.resourceDetails; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.willDoNothing; import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders; import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; @@ -14,9 +20,12 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import coffeemeet.server.auth.domain.RefreshToken; import coffeemeet.server.common.config.ControllerTestConfig; +import coffeemeet.server.common.fixture.AuthFixture; import coffeemeet.server.oauth.service.OAuthService; import coffeemeet.server.user.domain.OAuthProvider; +import coffeemeet.server.user.service.UserService; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; @@ -29,6 +38,9 @@ class OAuthControllerTest extends ControllerTestConfig { @MockBean private OAuthService oAuthService; + @MockBean + private UserService userService; + @DisplayName("sns 접근 권한 url로 redirect 할 수 있다.") @Test void redirectAuthCodeRequestUrlTest() throws Exception { @@ -59,4 +71,29 @@ void redirectAuthCodeRequestUrlTest() throws Exception { .andExpect(redirectedUrl(expectedRedirectUrl)); } + @DisplayName("사용자는 회원탈퇴 할 수 있다.") + @Test + void deleteTest() throws Exception { + // given + RefreshToken refreshToken = AuthFixture.refreshToken(); + willDoNothing().given(userService).deleteUser(anyLong(), anyString(), any()); + given(refreshTokenQuery.getRefreshToken(anyLong())).willReturn(refreshToken); + + // when, then + mockMvc.perform(post("/api/v1/oauth2.0/delete") + .header("Authorization", TOKEN) + .contentType(MediaType.APPLICATION_JSON) + ) + .andDo(document("auth-delete", + resourceDetails().tag("인증").description("회원탈퇴"), + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName("Authorization").description("토큰") + ) + ) + ) + .andExpect(status().isOk()); + } + } diff --git a/src/test/java/coffeemeet/server/oauth/service/OAuthServiceTest.java b/src/test/java/coffeemeet/server/oauth/service/OAuthServiceTest.java index fcf87897..8686c6bc 100644 --- a/src/test/java/coffeemeet/server/oauth/service/OAuthServiceTest.java +++ b/src/test/java/coffeemeet/server/oauth/service/OAuthServiceTest.java @@ -3,8 +3,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; -import coffeemeet.server.oauth.implement.client.OAuthMemberClientComposite; -import coffeemeet.server.oauth.implement.provider.AuthCodeRequestUrlProviderComposite; +import coffeemeet.server.oauth.implement.client.OAuthMemberClientRegistry; +import coffeemeet.server.oauth.implement.provider.AuthCodeRequestUrlProviderRegistry; import coffeemeet.server.user.domain.OAuthProvider; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -20,10 +20,10 @@ class OAuthServiceTest { private OAuthService oAuthService; @Mock - private AuthCodeRequestUrlProviderComposite authCodeRequestUrlProviderComposite; + private AuthCodeRequestUrlProviderRegistry authCodeRequestUrlProviderRegistry; @Mock - private OAuthMemberClientComposite oAuthMemberClientComposite; + private OAuthMemberClientRegistry oAuthMemberClientRegistry; @DisplayName("로그인 타입에 맞는 redirect url 을 생성할 수 있다.") @Test @@ -31,7 +31,7 @@ void getAuthCodeRequestUrlTest() { // given String expectedUrl = "https://example.com"; - given(authCodeRequestUrlProviderComposite.provide(OAuthProvider.KAKAO)).willReturn(expectedUrl); + given(authCodeRequestUrlProviderRegistry.provide(OAuthProvider.KAKAO)).willReturn(expectedUrl); // when String result = oAuthService.getAuthCodeRequestUrl(OAuthProvider.KAKAO); diff --git a/src/test/java/coffeemeet/server/user/presentation/UserControllerTest.java b/src/test/java/coffeemeet/server/user/presentation/UserControllerTest.java index cbae9d8b..b064f78d 100644 --- a/src/test/java/coffeemeet/server/user/presentation/UserControllerTest.java +++ b/src/test/java/coffeemeet/server/user/presentation/UserControllerTest.java @@ -44,6 +44,7 @@ import coffeemeet.server.user.presentation.dto.UpdateProfileHTTP; import coffeemeet.server.user.presentation.dto.UserProfileHTTP; import coffeemeet.server.user.presentation.dto.UserStatusHTTP; +import coffeemeet.server.user.service.UserProfileService; import coffeemeet.server.user.service.UserService; import coffeemeet.server.user.service.dto.LoginDetailsDto; import coffeemeet.server.user.service.dto.MyProfileDto; @@ -65,6 +66,9 @@ class UserControllerTest extends ControllerTestConfig { @MockBean private UserService userService; + @MockBean + private UserProfileService userProfileService; + @BeforeEach void setUp() { RefreshToken refreshToken = AuthFixture.refreshToken(); @@ -150,7 +154,7 @@ void findUserProfileTest() throws Exception { UserProfileHTTP.Response expectedResponse = UserFixture.userProfileHTTPResponse( response); - given(userService.findUserProfile(anyLong())).willReturn(response); + given(userProfileService.findUserProfile(anyLong())).willReturn(response); // when, then mockMvc.perform(get("/api/v1/users/{id}", USER_ID) @@ -184,7 +188,7 @@ void findMyProfileTest() throws Exception { MyProfileHTTP.Response expectedResponse = UserFixture.myProfileHTTPResponse(response); given(jwtTokenProvider.extractUserId(TOKEN)).willReturn(USER_ID); - given(userService.findMyProfile(anyLong())).willReturn(response); + given(userProfileService.findMyProfile(anyLong())).willReturn(response); // when, then mockMvc.perform(get("/api/v1/users/me") @@ -249,7 +253,7 @@ void updateProfileInfoTest() throws Exception { given(jwtTokenProvider.extractUserId(TOKEN)).willReturn(USER_ID); willDoNothing().given( - userService).updateProfileInfo(any(), any(), any()); + userProfileService).updateProfileInfo(any(), any(), any()); // when, then mockMvc.perform(patch("/api/v1/users/me") diff --git a/src/test/java/coffeemeet/server/user/service/UserProfileServiceTest.java b/src/test/java/coffeemeet/server/user/service/UserProfileServiceTest.java new file mode 100644 index 00000000..a4d1a542 --- /dev/null +++ b/src/test/java/coffeemeet/server/user/service/UserProfileServiceTest.java @@ -0,0 +1,156 @@ +package coffeemeet.server.user.service; + +import static coffeemeet.server.common.domain.S3KeyPrefix.PROFILE_IMAGE; +import static coffeemeet.server.common.fixture.CertificationFixture.certification; +import static coffeemeet.server.common.fixture.UserFixture.keywords; +import static coffeemeet.server.common.fixture.UserFixture.user; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.willDoNothing; +import static org.mockito.Mockito.verify; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; + +import coffeemeet.server.certification.domain.Certification; +import coffeemeet.server.certification.implement.CertificationQuery; +import coffeemeet.server.common.infrastructure.S3ObjectStorage; +import coffeemeet.server.user.domain.Keyword; +import coffeemeet.server.user.domain.User; +import coffeemeet.server.user.implement.InterestCommand; +import coffeemeet.server.user.implement.InterestQuery; +import coffeemeet.server.user.implement.UserCommand; +import coffeemeet.server.user.implement.UserQuery; +import coffeemeet.server.user.service.dto.MyProfileDto; +import coffeemeet.server.user.service.dto.UserProfileDto; +import jakarta.transaction.Transactional; +import java.io.File; +import java.io.IOException; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class UserProfileServiceTest { + + @InjectMocks + private UserProfileService userProfileService; + + @Mock + private S3ObjectStorage objectStorage; + + @Mock + private UserQuery userQuery; + + @Mock + private InterestQuery interestQuery; + + @Mock + private CertificationQuery certificationQuery; + + @Mock + private UserCommand userCommand; + + @Mock + private InterestCommand interestCommand; + + @DisplayName("사용자의 프로필을 조회할 수 있다.") + @Test + void findUserProfileTest() { + // given + User user = user(); + Certification certification = certification(); + List keywords = keywords(); + UserProfileDto response = UserProfileDto.of(user, keywords, certification); + + given(userQuery.getUserById(anyLong())).willReturn(user); + given(interestQuery.getKeywordsByUserId(anyLong())).willReturn(keywords); + given(certificationQuery.getCertificationByUserId(anyLong())).willReturn(certification); + + // when + UserProfileDto result = userProfileService.findUserProfile(user.getId()); + + // then + assertAll( + () -> assertThat(result.nickname()).isEqualTo(response.nickname()), + () -> assertThat(result.department()).isEqualTo(response.department()), + () -> assertThat(result.profileImageUrl()).isEqualTo(response.profileImageUrl()) + ); + } + + @DisplayName("본인의 프로필을 조회할 수 있다.") + @Test + void findMyProfileTest() { + // given + User user = user(); + Certification certification = certification(user); + List keywords = keywords(); + MyProfileDto response = MyProfileDto.of(user, keywords, certification); + + given(userQuery.getUserById(anyLong())).willReturn(user); + given(interestQuery.getKeywordsByUserId(anyLong())).willReturn(response.interests()); + given(certificationQuery.getCertificationByUserId(anyLong())).willReturn(certification); + + // when + MyProfileDto result = userProfileService.findMyProfile(user.getId()); + + // then + assertAll( + () -> assertThat(result.nickname()).isEqualTo(response.nickname()), + () -> assertThat(result.profileImageUrl()).isEqualTo(response.profileImageUrl()), + () -> assertThat(result.companyName()).isEqualTo(response.companyName()), + () -> assertThat(result.department()).isEqualTo(response.department()), + () -> assertThat(result.department()).isEqualTo(response.department()) + ); + } + + + @DisplayName("프로필 사진을 수정할 수 있다.") + @Test + void updateProfileImage() throws IOException { + // given + User user = user(); + File file = File.createTempFile("temp", "png"); + + given(userQuery.getUserById(anyLong())).willReturn(user); + given(objectStorage.generateKey(PROFILE_IMAGE)).willReturn("key"); + given(objectStorage.getUrl(anyString())).willReturn("newImageUrl"); + given(objectStorage.extractKey(any(), eq(PROFILE_IMAGE))).willReturn(""); + + // when + userProfileService.updateProfileImage(user.getId(), file); + + // then + assertThat(user.getOauthInfo().getProfileImageUrl()).isEqualTo("newImageUrl"); + } + + @DisplayName("프로필 정보를 수정할 수 있다.") + @Transactional + @Test + void updateProfileInfo() { + // given + User user = user(); + + String newNickname = "새닉네임"; + List newKeywords = keywords(); + + given(userQuery.getUserById(any())).willReturn(user); + willDoNothing().given(userCommand).updateUserInfo(any(), any()); + willDoNothing().given(interestCommand).updateInterests(any(), any()); + + // when + userProfileService.updateProfileInfo(user.getId(), newNickname, newKeywords); + + // then + verify(userCommand).updateUserInfo(any(User.class), anyString()); + verify(interestCommand).updateInterests(any(User.class), anyList()); + } + +} \ No newline at end of file diff --git a/src/test/java/coffeemeet/server/user/service/UserServiceTest.java b/src/test/java/coffeemeet/server/user/service/UserServiceTest.java index b4fd2295..323ced6e 100644 --- a/src/test/java/coffeemeet/server/user/service/UserServiceTest.java +++ b/src/test/java/coffeemeet/server/user/service/UserServiceTest.java @@ -1,6 +1,5 @@ package coffeemeet.server.user.service; -import static coffeemeet.server.common.domain.S3KeyPrefix.PROFILE_IMAGE; import static coffeemeet.server.common.fixture.AuthFixture.authTokens; import static coffeemeet.server.common.fixture.CertificationFixture.certification; import static coffeemeet.server.common.fixture.ChattingFixture.chattingRoom; @@ -19,23 +18,23 @@ import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; import static org.mockito.BDDMockito.willDoNothing; import static org.mockito.Mockito.only; -import static org.mockito.Mockito.verify; import coffeemeet.server.auth.domain.AuthTokens; import coffeemeet.server.auth.domain.AuthTokensGenerator; +import coffeemeet.server.auth.implement.RefreshTokenCommand; import coffeemeet.server.certification.domain.Certification; import coffeemeet.server.certification.implement.CertificationQuery; -import coffeemeet.server.common.domain.ObjectStorage; import coffeemeet.server.common.fixture.UserFixture; import coffeemeet.server.matching.implement.MatchingQueueCommand; import coffeemeet.server.oauth.domain.OAuthMemberDetail; -import coffeemeet.server.oauth.implement.client.OAuthMemberClientComposite; +import coffeemeet.server.oauth.implement.client.OAuthMemberClientRegistry; +import coffeemeet.server.oauth.implement.client.OAuthMemberUnlinkRegistry; import coffeemeet.server.user.domain.Keyword; +import coffeemeet.server.user.domain.OAuthProvider; import coffeemeet.server.user.domain.User; import coffeemeet.server.user.domain.UserStatus; import coffeemeet.server.user.implement.InterestCommand; @@ -44,11 +43,7 @@ import coffeemeet.server.user.implement.UserQuery; import coffeemeet.server.user.presentation.dto.SignupHTTP; import coffeemeet.server.user.service.dto.LoginDetailsDto; -import coffeemeet.server.user.service.dto.MyProfileDto; -import coffeemeet.server.user.service.dto.UserProfileDto; import coffeemeet.server.user.service.dto.UserStatusDto; -import java.io.File; -import java.io.IOException; import java.time.LocalDateTime; import java.util.List; import org.junit.jupiter.api.DisplayName; @@ -58,7 +53,6 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.transaction.annotation.Transactional; @ExtendWith(MockitoExtension.class) class UserServiceTest { @@ -67,10 +61,10 @@ class UserServiceTest { private UserService userService; @Mock - private ObjectStorage objectStorage; + private OAuthMemberClientRegistry oAuthMemberClientRegistry; @Mock - private OAuthMemberClientComposite oAuthMemberClientComposite; + private OAuthMemberUnlinkRegistry oAuthMemberUnlinkRegistry; @Mock private AuthTokensGenerator authTokensGenerator; @@ -87,6 +81,9 @@ class UserServiceTest { @Mock private UserCommand userCommand; + @Mock + private RefreshTokenCommand refreshTokenCommand; + @Mock private CertificationQuery certificationQuery; @@ -123,7 +120,7 @@ void loginTest() { LoginDetailsDto expectedResponse = LoginDetailsDto.of(user, keywords, certification, authTokens); - given(oAuthMemberClientComposite.fetch(any(), anyString())).willReturn(response); + given(oAuthMemberClientRegistry.fetch(any(), anyString())).willReturn(response); given(userQuery.getUserByOAuthInfoOrDefault(any())).willReturn(user); given(interestQuery.getKeywordsByUserId(anyLong())).willReturn(keywords); given(certificationQuery.getCertificationByUserId(anyLong())).willReturn(certification); @@ -145,55 +142,6 @@ void loginTest() { ); } - @DisplayName("사용자의 프로필을 조회할 수 있다.") - @Test - void findUserProfileTest() { - // given - User user = user(); - Certification certification = certification(); - List keywords = keywords(); - UserProfileDto response = UserProfileDto.of(user, keywords, certification); - - given(userQuery.getUserById(anyLong())).willReturn(user); - given(interestQuery.getKeywordsByUserId(anyLong())).willReturn(keywords); - given(certificationQuery.getCertificationByUserId(anyLong())).willReturn(certification); - - // when - UserProfileDto result = userService.findUserProfile(user.getId()); - - // then - assertAll( - () -> assertThat(result.nickname()).isEqualTo(response.nickname()), - () -> assertThat(result.department()).isEqualTo(response.department()), - () -> assertThat(result.profileImageUrl()).isEqualTo(response.profileImageUrl()) - ); - } - - @DisplayName("본인의 프로필을 조회할 수 있다.") - @Test - void findMyProfileTest() { - // given - User user = user(); - Certification certification = certification(user); - List keywords = keywords(); - MyProfileDto response = MyProfileDto.of(user, keywords, certification); - - given(userQuery.getUserById(anyLong())).willReturn(user); - given(interestQuery.getKeywordsByUserId(anyLong())).willReturn(response.interests()); - given(certificationQuery.getCertificationByUserId(anyLong())).willReturn(certification); - - // when - MyProfileDto result = userService.findMyProfile(user.getId()); - - // then - assertAll( - () -> assertThat(result.nickname()).isEqualTo(response.nickname()), - () -> assertThat(result.profileImageUrl()).isEqualTo(response.profileImageUrl()), - () -> assertThat(result.companyName()).isEqualTo(response.companyName()), - () -> assertThat(result.department()).isEqualTo(response.department()), - () -> assertThat(result.department()).isEqualTo(response.department()) - ); - } @DisplayName("아이디로 사용자를 조회할 수 있다.") @Test @@ -217,58 +165,22 @@ void getUserById() { ); } - @DisplayName("프로필 사진을 수정할 수 있다.") + @DisplayName("탈퇴할 수 있다.") @Test - void updateProfileImage() throws IOException { + void deleteUser() { // given User user = user(); - File file = File.createTempFile("temp", "png"); + String accessToken = "accessToken"; given(userQuery.getUserById(anyLong())).willReturn(user); - given(objectStorage.generateKey(PROFILE_IMAGE)).willReturn("key"); - given(objectStorage.getUrl(anyString())).willReturn("newImageUrl"); - given(objectStorage.extractKey(any(), eq(PROFILE_IMAGE))).willReturn(""); // when - userService.updateProfileImage(user.getId(), file); + userService.deleteUser(user.getId(), accessToken, KAKAO); // then - assertThat(user.getOauthInfo().getProfileImageUrl()).isEqualTo("newImageUrl"); - } - - @DisplayName("프로필 정보를 수정할 수 있다.") - @Transactional - @Test - void updateProfileInfo() { - // given - User user = user(); - - String newNickname = "새닉네임"; - List newKeywords = keywords(); - - given(userQuery.getUserById(any())).willReturn(user); - willDoNothing().given(userCommand).updateUserInfo(any(), any()); - willDoNothing().given(interestCommand).updateInterests(any(), any()); - - // when - userService.updateProfileInfo(user.getId(), newNickname, newKeywords); - - // then - verify(userCommand).updateUserInfo(any(User.class), anyString()); - verify(interestCommand).updateInterests(any(User.class), anyList()); - } - - @DisplayName("탈퇴할 수 있다.") - @Test - void deleteUser() { - // given - User user = user(); - - willDoNothing().given(userCommand).deleteUser(user.getId()); - - // when, then - assertThatCode(() -> userService.deleteUser(user.getId())) - .doesNotThrowAnyException(); + assertTrue(user.isDeleted()); + assertNotNull(user.getPrivacyDateTime()); + assertNotNull(userQuery.getUserById(user.getId())); } @DisplayName("닉네임 중복을 검사할 수 있다.") @@ -415,4 +327,19 @@ void getUserStatusReportedTest() { } } + @DisplayName("회원 탈퇴 시킬 수 있다.") + @Test + void deleteTest() { + // given + User user = user(); + Long userId = user.getId(); + String accessToken = "accessToken"; + + given(userQuery.getUserById(anyLong())).willReturn(user); + + // when, then + assertThatCode(() -> userService.deleteUser(userId, accessToken, OAuthProvider.KAKAO)) + .doesNotThrowAnyException(); + } + } From f7b4a7b75fdf9399bf35804c697d8b5c8749fb1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Wed, 7 Feb 2024 10:10:56 +0900 Subject: [PATCH 14/15] =?UTF-8?q?refactor:=20=EC=8A=A4=EC=BC=80=EC=A4=84?= =?UTF-8?q?=EB=9F=AC=20=EC=84=A4=EC=A0=95=20=EC=96=B4=EB=85=B8=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=9C=84=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/coffeemeet/server/ServerApplication.java | 2 -- .../config/{SchedulingConfig.java => ScheduleConfig.java} | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/main/java/coffeemeet/server/common/config/{SchedulingConfig.java => ScheduleConfig.java} (82%) diff --git a/src/main/java/coffeemeet/server/ServerApplication.java b/src/main/java/coffeemeet/server/ServerApplication.java index ae042a7f..ec48a22b 100644 --- a/src/main/java/coffeemeet/server/ServerApplication.java +++ b/src/main/java/coffeemeet/server/ServerApplication.java @@ -2,10 +2,8 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication -@EnableScheduling public class ServerApplication { public static void main(String[] args) { diff --git a/src/main/java/coffeemeet/server/common/config/SchedulingConfig.java b/src/main/java/coffeemeet/server/common/config/ScheduleConfig.java similarity index 82% rename from src/main/java/coffeemeet/server/common/config/SchedulingConfig.java rename to src/main/java/coffeemeet/server/common/config/ScheduleConfig.java index dd5b2344..5d6371b0 100644 --- a/src/main/java/coffeemeet/server/common/config/SchedulingConfig.java +++ b/src/main/java/coffeemeet/server/common/config/ScheduleConfig.java @@ -3,13 +3,15 @@ import coffeemeet.server.user.service.UserService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Slf4j @Component +@EnableScheduling @RequiredArgsConstructor -public class SchedulingConfig { +public class ScheduleConfig { private final UserService userService; From 45ee79306143c599816dcd22c76848dd8e46d933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=8B=E1=85=B3=E1=86=AB?= =?UTF-8?q?=E1=84=8C=E1=85=B5?= Date: Wed, 7 Feb 2024 10:46:56 +0900 Subject: [PATCH 15/15] =?UTF-8?q?refactor:=20=EC=9C=A0=EC=A0=80=20?= =?UTF-8?q?=ED=83=88=ED=87=B4=20=EB=B0=8F=20=EA=B0=80=EC=9E=85=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/oauth/service/OAuthService.java | 9 ++-- .../coffeemeet/server/user/domain/User.java | 23 +++++----- .../server/user/implement/UserCommand.java | 5 +++ .../server/user/implement/UserQuery.java | 2 +- .../user/infrastructure/UserRepository.java | 3 +- .../server/user/service/UserService.java | 42 +++++++------------ 6 files changed, 40 insertions(+), 44 deletions(-) diff --git a/src/main/java/coffeemeet/server/oauth/service/OAuthService.java b/src/main/java/coffeemeet/server/oauth/service/OAuthService.java index c0a37a3e..ba3a615e 100644 --- a/src/main/java/coffeemeet/server/oauth/service/OAuthService.java +++ b/src/main/java/coffeemeet/server/oauth/service/OAuthService.java @@ -1,9 +1,10 @@ package coffeemeet.server.oauth.service; import coffeemeet.server.auth.implement.RefreshTokenCommand; +import coffeemeet.server.oauth.implement.client.OAuthMemberUnlinkRegistry; import coffeemeet.server.oauth.implement.provider.AuthCodeRequestUrlProviderRegistry; import coffeemeet.server.user.domain.OAuthProvider; -import coffeemeet.server.user.service.UserService; +import coffeemeet.server.user.implement.UserCommand; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -13,7 +14,8 @@ public class OAuthService { private final AuthCodeRequestUrlProviderRegistry authCodeRequestUrlProviderRegistry; - private final UserService userService; + private final OAuthMemberUnlinkRegistry oAuthMemberUnlinkRegistry; + private final UserCommand userCommand; private final RefreshTokenCommand refreshTokenCommand; public String getAuthCodeRequestUrl(OAuthProvider oAuthProvider) { @@ -22,7 +24,8 @@ public String getAuthCodeRequestUrl(OAuthProvider oAuthProvider) { @Transactional public void unlink(Long userId, String accessToken, OAuthProvider oAuthProvider) { - userService.deleteUser(userId, accessToken, oAuthProvider); + oAuthMemberUnlinkRegistry.unlink(oAuthProvider, accessToken); + userCommand.deleteUser(userId); refreshTokenCommand.deleteRefreshToken(userId); } diff --git a/src/main/java/coffeemeet/server/user/domain/User.java b/src/main/java/coffeemeet/server/user/domain/User.java index 95c66543..69efe717 100644 --- a/src/main/java/coffeemeet/server/user/domain/User.java +++ b/src/main/java/coffeemeet/server/user/domain/User.java @@ -64,12 +64,11 @@ public class User extends AdvancedBaseEntity { @Enumerated(EnumType.STRING) private UserStatus userStatus; + @Column(nullable = false) private boolean isDeleted; - @Column(name = "privacy_date_time") - private LocalDateTime privacyDateTime; - - private boolean isBlacklisted; + @Column(name = "updated_at") + private LocalDateTime updatedAt; @Column(nullable = false) private boolean isRegistered; @@ -83,8 +82,7 @@ public void registerUser(@NonNull Profile profile) { this.reportInfo = new ReportInfo(); this.userStatus = IDLE; this.isDeleted = false; - this.privacyDateTime = null; - this.isBlacklisted = false; + this.updatedAt = null; this.isRegistered = true; } @@ -142,17 +140,18 @@ public void matching() { this.userStatus = MATCHING; } - public void convertToBlacklist() { - this.isBlacklisted = true; - } - public void delete() { this.isDeleted = true; - this.privacyDateTime = LocalDateTime.now().plusDays(30); + this.updatedAt = LocalDateTime.now(); + } + + public boolean leave() { + return this.updatedAt.isBefore(LocalDateTime.now()); } - public void deletedWithdraw(){ + public void deletedWithdraw() { this.isDeleted = false; + this.updatedAt = null; } @Override diff --git a/src/main/java/coffeemeet/server/user/implement/UserCommand.java b/src/main/java/coffeemeet/server/user/implement/UserCommand.java index 45b52e9c..6beaa028 100644 --- a/src/main/java/coffeemeet/server/user/implement/UserCommand.java +++ b/src/main/java/coffeemeet/server/user/implement/UserCommand.java @@ -30,6 +30,11 @@ public void updateUser(User user) { } public void deleteUser(Long userId) { + User user = userQuery.getUserById(userId); + user.delete(); + } + + public void deleteUserInfo(Long userId) { interestRepository.deleteById(userId); userRepository.deleteById(userId); } diff --git a/src/main/java/coffeemeet/server/user/implement/UserQuery.java b/src/main/java/coffeemeet/server/user/implement/UserQuery.java index 9fb67499..0ffb8bfb 100644 --- a/src/main/java/coffeemeet/server/user/implement/UserQuery.java +++ b/src/main/java/coffeemeet/server/user/implement/UserQuery.java @@ -92,7 +92,7 @@ public List getUsersByRoom(ChattingRoom room) { } public List getDeletedUsers() { - return userRepository.findAllByIsDeletedIsTrue(); + return userRepository.findDeletedUsers(); } } diff --git a/src/main/java/coffeemeet/server/user/infrastructure/UserRepository.java b/src/main/java/coffeemeet/server/user/infrastructure/UserRepository.java index 4d2aa55f..bcffac81 100644 --- a/src/main/java/coffeemeet/server/user/infrastructure/UserRepository.java +++ b/src/main/java/coffeemeet/server/user/infrastructure/UserRepository.java @@ -25,6 +25,7 @@ public interface UserRepository extends JpaRepository { List findAllByChattingRoom(ChattingRoom chattingRoom); - List findAllByIsDeletedIsTrue(); + @Query(value = "select * from users where is_deleted = true", nativeQuery = true) + List findDeletedUsers(); } diff --git a/src/main/java/coffeemeet/server/user/service/UserService.java b/src/main/java/coffeemeet/server/user/service/UserService.java index 1246d7ff..377c6d18 100644 --- a/src/main/java/coffeemeet/server/user/service/UserService.java +++ b/src/main/java/coffeemeet/server/user/service/UserService.java @@ -25,7 +25,6 @@ import coffeemeet.server.user.service.dto.LoginDetailsDto; import coffeemeet.server.user.service.dto.UserStatusDto; import java.time.LocalDateTime; -import java.util.Collections; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -38,7 +37,6 @@ public class UserService { private static final String INVALID_REQUEST_MESSAGE = "사용자 상태에 맞지 않는 요청입니다."; private final OAuthMemberClientRegistry oAuthMemberClientRegistry; - private final OAuthMemberUnlinkRegistry oAuthMemberUnlinkRegistry; private final CertificationQuery certificationQuery; private final AuthTokensGenerator authTokensGenerator; @@ -66,48 +64,38 @@ public LoginDetailsDto login(OAuthProvider oAuthProvider, String authCode) { if (user.isRegistered()) { // TODO: 12/21/23 회원가입 중간에 나갈 때 예외 터지는 오류 잡기 - if (user.isDeleted()) { - if (user.getPrivacyDateTime().isBefore(LocalDateTime.now())) { - user.deletedWithdraw(); - userCommand.updateUser(user); - List interests = interestQuery.getKeywordsByUserId(user.getId()); - Certification certification = certificationQuery.getCertificationByUserId(user.getId()); - AuthTokens authTokens = authTokensGenerator.generate(user.getId()); - return LoginDetailsDto.of(user, interests, certification, authTokens); - } else { - userCommand.deleteUser(user.getId()); - } + if (user.leave()) { + // 연결 끊기 + userCommand.deleteUserInfo(user.getId()); + return LoginDetailsDto.of(null, null, null, null); } else { - List interests = interestQuery.getKeywordsByUserId(user.getId()); - Certification certification = certificationQuery.getCertificationByUserId(user.getId()); + user.deletedWithdraw(); AuthTokens authTokens = authTokensGenerator.generate(user.getId()); - return LoginDetailsDto.of(user, interests, certification, authTokens); + return LoginDetailsDto.of(user, null, null, authTokens); } } - userCommand.saveUser(user); - return LoginDetailsDto.of(user, Collections.emptyList(), null, null); + Long userId = userCommand.saveUser(user); + List interests = interestQuery.getKeywordsByUserId(userId); + Certification certification = certificationQuery.getCertificationByUserId(userId); + AuthTokens authTokens = authTokensGenerator.generate(userId); + return LoginDetailsDto.of(user, interests, certification, authTokens); } public void checkDuplicatedNickname(String nickname) { userQuery.hasDuplicatedNickname(nickname); } - public void deleteUser(Long userId, String accessToken, OAuthProvider oAuthProvider) { - User user = userQuery.getUserById(userId); - user.delete(); - oAuthMemberUnlinkRegistry.unlink(oAuthProvider, accessToken); - } - public void deleteUserInfos() { List users = userQuery.getDeletedUsers(); LocalDateTime today = LocalDateTime.now(); List deletedUsers = users.stream() - .filter(u -> u.getPrivacyDateTime() != null) + .filter(user -> user.getUpdatedAt() != null) .filter( - u -> u.getPrivacyDateTime().isAfter(today) || u.getPrivacyDateTime().isEqual(today)) + user -> user.getUpdatedAt().isAfter(today) || user.getUpdatedAt() + .isEqual(today)) .toList(); - deletedUsers.forEach(u -> userCommand.deleteUser(u.getId())); + deletedUsers.forEach(u -> userCommand.deleteUserInfo(u.getId())); } public void registerOrUpdateNotificationToken(Long useId, String token) {