Skip to content

Commit

Permalink
[FEATURE] 회원 탈퇴 구현 (#145)
Browse files Browse the repository at this point in the history
* feat : 애플 로그인 연동 해제 구현

* feat : 회원 탈퇴 구현 - 로그인 & 회원가입 관련 #142

* feat : 회원 탈퇴 구현 - 유저, 답변 관련 로직 수정 #142

* feat : validation 추가 #142
  • Loading branch information
bongsh0112 authored Jan 10, 2024
1 parent 76789ce commit a86a897
Show file tree
Hide file tree
Showing 43 changed files with 483 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,20 @@
import tify.server.domain.domains.question.adaptor.AnswerReportAdaptor;
import tify.server.domain.domains.question.domain.AnswerReport;
import tify.server.domain.domains.question.validator.AnswerReportValidator;
import tify.server.domain.domains.question.validator.AnswerValidator;

@UseCase
@RequiredArgsConstructor
public class CreateDailyAnswerReportUseCase {

private final AnswerReportAdaptor answerReportAdaptor;
private final AnswerReportValidator answerReportValidator;
private final AnswerValidator answerValidator;

@Transactional
public AnswerReportResponse execute(Long answerId) {
Long currentUserId = SecurityUtils.getCurrentUserId();
answerValidator.isValidAnswer(answerId);
Optional<AnswerReport> report =
answerReportAdaptor.optionalQueryByUserIdAndAnswerId(currentUserId, answerId);
if (report.isPresent()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public class CreateKnockUseCase {
@Transactional
public void execute(Long questionId, Long userId) {
Long currentUserId = SecurityUtils.getCurrentUserId();
userValidator.isValidUser(userId);
userValidator.isResignedUser(userId); // 탈퇴한 유저인지 검증
userValidator.isNeighbor(currentUserId, userId); // 친구인지를 검증
questionValidator.isValidateAnswerToQuestion(
questionId, userId); // 오늘 날짜의 질문이 맞는지, 친구가 이미 답을 남겼는지 검증
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import tify.server.core.annotation.UseCase;
import tify.server.domain.domains.question.adaptor.AnswerAdaptor;
import tify.server.domain.domains.question.adaptor.DailyQuestionAdaptor;
import tify.server.domain.domains.question.domain.DailyQuestion;

@UseCase
@RequiredArgsConstructor
Expand All @@ -18,7 +17,6 @@ public class RetrieveDailyAnswerCountUseCase {

@Transactional(readOnly = true)
public RetrieveAnswerCountVo execute(Long questionId) {
DailyQuestion dailyQuestion = dailyQuestionAdaptor.query(questionId);
Long answerCount = answerAdaptor.queryAnswerCount(questionId);
return RetrieveAnswerCountVo.of(answerCount);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
import tify.server.domain.domains.question.domain.DailyQuestion;
import tify.server.domain.domains.question.dto.condition.AnswerCondition;
import tify.server.domain.domains.user.adaptor.NeighborAdaptor;
import tify.server.domain.domains.user.adaptor.UserResignAdaptor;
import tify.server.domain.domains.user.domain.Neighbor;
import tify.server.domain.domains.user.domain.UserResign;
import tify.server.domain.domains.user.dto.model.RetrieveNeighborDTO;

@Slf4j
Expand All @@ -31,15 +33,19 @@ public class RetrieveDailyAnswerUseCase {
private final NeighborAdaptor neighborAdaptor;
private final DailyQuestionAdaptor dailyQuestionAdaptor;
private final KnockAdaptor knockAdaptor;
private final UserResignAdaptor userResignAdaptor;

@Transactional(readOnly = true)
public List<RetrieveAnswerVo> execute(Long questionId) {
DailyQuestion dailyQuestion = dailyQuestionAdaptor.query(questionId);
Long currentUserId = SecurityUtils.getCurrentUserId();
List<Long> resignedUserId =
userResignAdaptor.queryAll().stream().map(UserResign::getUserId).toList();
List<Long> userIdList =
new ArrayList<>(
neighborAdaptor.queryAllByFromUserIdAndIsView(currentUserId, true).stream()
.map(Neighbor::getToUserId)
.filter(id -> !resignedUserId.contains(id))
.toList());
userIdList.add(currentUserId);
AnswerCondition answerCondition = new AnswerCondition(dailyQuestion.getId(), userIdList);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,20 @@
import tify.server.api.config.security.SecurityUtils;
import tify.server.core.annotation.UseCase;
import tify.server.domain.domains.question.adaptor.KnockAdaptor;
import tify.server.domain.domains.user.validator.UserValidator;

@UseCase
@RequiredArgsConstructor
public class RetrieveKnockUseCase {

private final KnockAdaptor knockAdaptor;
private final UserValidator userValidator;

@Transactional(readOnly = true)
public KnockCountVo executeCount(Long questionId, Long userId) {
Long currentUserId = SecurityUtils.getCurrentUserId();
userValidator.isValidUser(userId);
userValidator.isResignedUser(userId);
int size =
knockAdaptor
.queryAllByDailyQuestionIdAndUserIdAndKnockedUserId(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
import javax.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.DeleteMapping;
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.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
Expand All @@ -24,18 +26,20 @@
import tify.server.api.auth.model.response.UserRefreshTokenResponse;
import tify.server.api.auth.service.LoginUseCase;
import tify.server.api.auth.service.LogoutUseCase;
import tify.server.api.auth.service.ResignUseCase;
import tify.server.api.auth.service.SignUpUseCase;

@RestController
@Slf4j
@RestController
@Tag(name = "1. [인증]")
@RequestMapping(value = "/auth")
@RequiredArgsConstructor
@Tag(name = "1. [인증]")
public class AuthController {

private final SignUpUseCase signUpUseCase;
private final LoginUseCase loginUseCase;
private final LogoutUseCase logoutUseCase;
private final ResignUseCase resignUseCase;

@Deprecated
@Operation(summary = "kakao oauth 링크발급 (백엔드용)", description = "kakao 링크를 받아볼수 있습니다.")
Expand Down Expand Up @@ -174,5 +178,22 @@ public OauthRefreshResponse validateRefreshToken(@RequestParam String refreshTok
return loginUseCase.getCredentialFromApple(refreshToken);
}

// Todo: 회원 탈퇴 구현
@Operation(summary = "애플 로그인 연동 해제", description = "애플 로그인 연동 해제 후에는 회원 탈퇴를 실행해야 합니다.")
@PostMapping("/oauth/apple/revoke/{userId}")
public void revokeAppleLogin(@PathVariable Long userId)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
resignUseCase.revokeAppleToken(userId);
}

@Operation(summary = "회원 탈퇴")
@PostMapping("/oauth/resign/{userId}")
public void resign(@PathVariable Long userId) {
resignUseCase.execute(userId);
}

@Operation(summary = "회원 탈퇴 해제")
@DeleteMapping("/oauth/resign/{userId}")
public void deleteResign(@PathVariable Long userId) {
resignUseCase.delete(userId);
}
}
59 changes: 59 additions & 0 deletions Api/src/main/java/tify/server/api/auth/service/ResignUseCase.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package tify.server.api.auth.service;


import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import lombok.RequiredArgsConstructor;
import org.springframework.transaction.annotation.Transactional;
import tify.server.core.annotation.UseCase;
import tify.server.core.jwt.JwtTokenProvider;
import tify.server.core.properties.OauthProperties;
import tify.server.domain.domains.user.adaptor.UserAdaptor;
import tify.server.domain.domains.user.adaptor.UserResignAdaptor;
import tify.server.domain.domains.user.domain.User;
import tify.server.domain.domains.user.domain.UserResign;
import tify.server.domain.domains.user.validator.UserValidator;
import tify.server.infrastructure.outer.api.oauth.client.AppleRevokeClient;
import tify.server.infrastructure.outer.api.oauth.dto.AppleRevokeRequest;

@UseCase
@RequiredArgsConstructor
public class ResignUseCase {

private final UserAdaptor userAdaptor;
private final UserValidator userValidator;
private final UserResignAdaptor userResignAdaptor;
private final OauthProperties oauthProperties;
private final JwtTokenProvider jwtTokenProvider;
private final AppleRevokeClient appleRevokeClient;

@Transactional
public void execute(Long userId) {
User user = userAdaptor.query(userId);
userValidator.isNewResign(user.getOauthInfo());
userResignAdaptor.save(
UserResign.builder().userId(userId).oauthInfo(user.getOauthInfo()).build());
}

@Transactional(readOnly = true)
public void revokeAppleToken(Long userId)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
User user = userAdaptor.query(userId);
String refreshToken = user.getAppleRefreshToken();
AppleRevokeRequest request =
AppleRevokeRequest.builder()
.client_id(oauthProperties.getAppleClientUrl())
.client_secret(jwtTokenProvider.buildAppleClientSecret())
.token(refreshToken)
.type("refresh_token")
.build();
appleRevokeClient.appleRevoke(request);
}

@Transactional
public void delete(Long userId) {
UserResign userResign = userResignAdaptor.queryByUserId(userId);
userResignAdaptor.delete(userResign);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@
import tify.server.api.question.model.request.PostAnswerRequest;
import tify.server.core.annotation.UseCase;
import tify.server.domain.domains.question.service.DailyQuestionDomainService;
import tify.server.domain.domains.question.validator.QuestionValidator;

@UseCase
@RequiredArgsConstructor
public class CreateAnswerUseCase {

private final DailyQuestionDomainService dailyQuestionDomainService;
private final QuestionValidator questionValidator;

public void execute(Long questionId, PostAnswerRequest body) {
questionValidator.isValidDailyQuestion(questionId);
dailyQuestionDomainService.createAnswer(
questionId, SecurityUtils.getCurrentUserId(), body.getAnswer());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import tify.server.domain.domains.user.domain.NeighborApplication;
import tify.server.domain.domains.user.exception.AlreadyExistNeighborRelationshipException;
import tify.server.domain.domains.user.validator.NeighborValidator;
import tify.server.domain.domains.user.validator.UserValidator;

@UseCase
@RequiredArgsConstructor
Expand All @@ -19,6 +20,7 @@ public class AcceptanceNeighborApplicationUseCase {

private final NeighborAdaptor neighborAdaptor;
private final NeighborValidator neighborValidator;
private final UserValidator userValidator;

public void execute(Long neighborApplicationId) {

Expand All @@ -33,6 +35,7 @@ public void execute(Long neighborApplicationId) {

// 상대방 (친구 신청을 보낸 사람)
Long toUserId = neighborApplication.getFromUserId();
userValidator.isValidUser(toUserId);

// 친구 맺기 (toUserId, fromUserId inversion)
if (neighborAdaptor.existsNeighbor(currentUserId, toUserId)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import tify.server.domain.domains.user.domain.User;
import tify.server.domain.domains.user.exception.AlreadyExistNeighborRelationshipException;
import tify.server.domain.domains.user.exception.SelfNeighborException;
import tify.server.domain.domains.user.validator.UserValidator;

@UseCase
@RequiredArgsConstructor
Expand All @@ -19,13 +20,16 @@ public class CreateNeighborUseCase {

private final UserAdaptor userAdaptor;
private final NeighborAdaptor neighborAdaptor;
private final UserValidator userValidator;

public void execute(Long toUserId) {
Long currentUserId = SecurityUtils.getCurrentUserId();

if (currentUserId.equals(toUserId)) {
throw SelfNeighborException.EXCEPTION;
}

userValidator.isValidUser(toUserId);
User toUser = userAdaptor.query(toUserId);

if (neighborAdaptor.existsNeighbor(currentUserId, toUser.getId())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,21 @@
import tify.server.domain.domains.user.adaptor.UserAdaptor;
import tify.server.domain.domains.user.adaptor.UserReportAdaptor;
import tify.server.domain.domains.user.domain.UserReport;
import tify.server.domain.domains.user.validator.UserValidator;

@UseCase
@RequiredArgsConstructor
public class CreateUserReportUseCase {

private final UserReportAdaptor userReportAdaptor;
private final UserAdaptor userAdaptor;
private final UserValidator userValidator;

@Transactional
public UserReportResponse execute(Long userId) {
Long currentUserId = SecurityUtils.getCurrentUserId();
userValidator.isValidUser(userId);
userValidator.isResignedUser(userId);
Optional<UserReport> report =
userReportAdaptor.optionalQueryByFromUserIdAndToUserId(currentUserId, userId);
if (report.isPresent()) { // 이미 신고가 존재할 때
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class NeighborInfoUseCase {

public UserProfileVo execute(Long neighborId) {
Long userId = SecurityUtils.getCurrentUserId();
userValidator.isValidUser(neighborId);
userValidator.isNeighbor(userId, neighborId);
User neighbor = userAdaptor.query(neighborId);
return neighbor.toUserProfileVo();
Expand All @@ -32,6 +33,7 @@ public UserProfileVo execute(Long neighborId) {
@Transactional
public void updateViewedAt(Long neighborId) {
Long userId = SecurityUtils.getCurrentUserId();
userValidator.isValidUser(neighborId);
userValidator.isNeighbor(userId, neighborId);
Neighbor neighbor =
neighborAdaptor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import tify.server.domain.domains.user.adaptor.NeighborAdaptor;
import tify.server.domain.domains.user.domain.NeighborApplication;
import tify.server.domain.domains.user.validator.NeighborValidator;
import tify.server.domain.domains.user.validator.UserValidator;

@UseCase
@RequiredArgsConstructor
Expand All @@ -16,12 +17,15 @@ public class RejectNeighborApplicationUseCase {

private final NeighborAdaptor neighborAdaptor;
private final NeighborValidator neighborValidator;
private final UserValidator userValidator;

public void execute(Long neighborApplicationId) {
Long currentUserId = SecurityUtils.getCurrentUserId();
NeighborApplication neighborApplication =
neighborAdaptor.queryNeighborApplication(neighborApplicationId);

userValidator.isValidUser(neighborApplication.getFromUserId());

neighborValidator.userIdAndNeighborApplicationValidate(neighborApplication, currentUserId);

neighborApplication.reject();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class RemoveNeighborUseCase {
public void execute(Long toUserId) {
Long userId = SecurityUtils.getCurrentUserId();
userValidator.isNeighbor(userId, toUserId);
userValidator.isResignedUser(toUserId);
neighborAdaptor
.queryByFromUserIdAndToUserId(userId, toUserId)
.ifPresent(neighborAdaptor::delete);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,34 @@
import tify.server.api.user.model.dto.vo.MutualFriendsVo;
import tify.server.core.annotation.UseCase;
import tify.server.domain.domains.user.adaptor.NeighborAdaptor;
import tify.server.domain.domains.user.adaptor.UserResignAdaptor;
import tify.server.domain.domains.user.domain.Neighbor;
import tify.server.domain.domains.user.domain.UserResign;

@UseCase
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class RetrieveMutualFriendsUseCase {

private final NeighborAdaptor neighborAdaptor;
private final UserResignAdaptor userResignAdaptor;

public MutualFriendsVo execute(Long toUserId) {
Long userId = SecurityUtils.getCurrentUserId();

List<Long> resignedUsers =
userResignAdaptor.queryAll().stream().map(UserResign::getUserId).toList();

List<Long> myNeighbors =
neighborAdaptor.queryAllByFromUserId(userId).stream()
.map(Neighbor::getToUserId)
.filter(id -> !resignedUsers.contains(id))
.toList();

List<Long> findUserNeighbors =
neighborAdaptor.queryAllByFromUserId(toUserId).stream()
.map(Neighbor::getToUserId)
.filter(id -> !resignedUsers.contains(id))
.toList();

int mutualFriendsCount =
Expand Down
Loading

0 comments on commit a86a897

Please sign in to comment.