Skip to content

Commit

Permalink
Merge pull request #127 from Team-Motivoo/fix/#126-timezone_error
Browse files Browse the repository at this point in the history
[FIX] 회원탈퇴, 오늘의 미션 선택지 관련 버그 해결
  • Loading branch information
jun02160 authored Mar 2, 2024
2 parents 1025ce9 + 24d9194 commit fe5ae6e
Show file tree
Hide file tree
Showing 19 changed files with 145 additions and 93 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package sopt.org.motivoo.api;

import java.util.TimeZone;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;

import jakarta.annotation.PostConstruct;
import sopt.org.motivoo.domain.MotivooDomainRoot;
import sopt.org.motivoo.common.MotivooCommonRoot;
import sopt.org.motivoo.external.MotivooExternalRoot;
Expand All @@ -20,4 +23,9 @@ public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}

@PostConstruct
public void setTimeZone(){
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul"));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package sopt.org.motivoo.api.controller.auth;

import static sopt.org.motivoo.common.response.SuccessType.*;
import static sopt.org.motivoo.domain.auth.config.jwt.JwtTokenProvider.*;

import java.security.Principal;

Expand Down Expand Up @@ -53,9 +54,7 @@ public ResponseEntity<ApiResponse<Object>> logout(@RequestHeader("Authorization"

@DeleteMapping("/withdraw")
public ResponseEntity<ApiResponse<Object>> signout(Principal principal) {
Long userId = Long.parseLong(principal.getName());
log.info("유저 아이디="+userId);
userService.deleteSocialAccount(userId);
oauthService.signout(getUserFromPrincipal(principal));

return ApiResponse.success(SIGNOUT_SUCCESS);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ public String getRefreshToken(String refreshToken) {


public void deleteRefreshToken(String refreshToken) {
tokenRedisRepository.deleteRefreshToken(refreshToken);
tokenRedisRepository.deleteRefreshToken(refreshToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,27 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import sopt.org.motivoo.domain.auth.config.UserAuthentication;
import sopt.org.motivoo.domain.auth.config.jwt.JwtTokenProvider;
import sopt.org.motivoo.domain.auth.dto.request.OauthTokenCommand;
import sopt.org.motivoo.domain.auth.dto.response.LoginResult;
import sopt.org.motivoo.domain.health.repository.HealthRetriever;
import sopt.org.motivoo.domain.mission.repository.UserMissionChoicesRetriever;
import sopt.org.motivoo.domain.mission.repository.UserMissionRetriever;
import sopt.org.motivoo.domain.parentchild.entity.Parentchild;
import sopt.org.motivoo.domain.parentchild.repository.ParentchildRetriever;
import sopt.org.motivoo.domain.user.service.UserManager;
import sopt.org.motivoo.external.client.auth.apple.service.dto.OAuthPlatformMemberResult;
import sopt.org.motivoo.domain.auth.repository.TokenRedisRetriever;
import sopt.org.motivoo.external.client.auth.apple.service.AppleLoginService;
Expand All @@ -39,6 +51,13 @@ public class OauthService {
private final InMemoryClientRegistrationRepository inMemoryRepository;
private final UserRetriever userRetriever;
private final TokenRedisRetriever tokenRedisRetriever;

private final UserManager userManager;
private final HealthRetriever healthRetriever;
private final UserMissionRetriever userMissionRetriever;
private final UserMissionChoicesRetriever userMissionChoicesRetriever;
private final ParentchildRetriever parentchildRetriever;

private final JwtTokenProvider jwtTokenProvider;
private final AppleLoginService appleLoginService;

Expand Down Expand Up @@ -161,4 +180,64 @@ public void logout(String accessToken) {
tokenRedisRetriever.saveBlockedToken(accessToken);
tokenRedisRetriever.deleteRefreshToken(refreshToken);
}


@Transactional
public void signout(Long userId) {

User user = userRetriever.getUserById(userId);
// List<User> sameSocialUsers = userRetriever.getUsersBySocialId(user.getSocialId()); // 동일한 소셜 계정으로 탈퇴-가입을 반복한 경우
tokenRedisRetriever.deleteRefreshToken(user.getRefreshToken());
userManager.withdrawUser(user);
healthRetriever.deleteByUser(user); // 온보딩 건강 정보 삭제
user.getUserMissionChoice().forEach(umc -> userMissionChoicesRetriever.deleteByUser(user));

Parentchild parentchild = user.getParentchild();
List<User> users = userRetriever.getUsersByParentchild(parentchild);

boolean allUsersDeleted = users.stream()
.allMatch(u -> u.getSocialPlatform().equals(WITHDRAW));
log.info("2명의 유저 모두 탈퇴? {}", allUsersDeleted);
if (allUsersDeleted) {
log.info("User Size: {}", users.size());
if (users.size() == 1) {
log.info("삭제된 유저: {}", users.get(0).getNickname());
} else if (users.size() == 2) {
log.info("삭제된 부모자식: {} X {}", users.get(0).getNickname(), users.get(1).getNickname());
}
parentchildRetriever.deleteById(parentchild.getId());

users.forEach(u -> {
u.getUserMissions().forEach(um -> userMissionRetriever.deleteByUser(user));
userRetriever.deleteById(u.getId());
});
}
}


// TODO 혜연 언니한테 질문
private void sendRevokeRequest(String data, SocialPlatform socialPlatform, String accessToken) {
// String appleRevokeUrl = "https://appleid.apple.com/auth/revoke"; //TODO 추후 애플 로그인 구현 후
String kakaoRevokeUrl = "https://kapi.kakao.com/v1/user/unlink";

RestTemplate restTemplate = new RestTemplate();
String revokeUrl = "";

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

HttpEntity<String> entity = new HttpEntity<>(data, headers);

switch (socialPlatform) {
// case APPLE -> revokeUrl = appleRevokeUrl;
case KAKAO -> {
revokeUrl = kakaoRevokeUrl;
headers.setBearerAuth(accessToken);
}
}

ResponseEntity<String> responseEntity = restTemplate.exchange(revokeUrl, HttpMethod.POST, entity, String.class);

log.info("response="+responseEntity);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package sopt.org.motivoo.domain.mission.entity;

import java.time.LocalDateTime;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.ConstraintMode;
Expand Down Expand Up @@ -40,4 +42,8 @@ public UserMissionChoices(Mission mission, User user) {
this.mission = mission;
this.user = user;
}

public void setCreatedAtNow(LocalDateTime dateTime) {
this.createdAt = dateTime;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

public interface UserMissionChoicesRepository extends JpaRepository<UserMissionChoices, Long> {

void deleteByUser(User user);
void deleteAllByUser(User user);

@Query("SELECT umc FROM UserMissionChoices umc WHERE umc.user = :user AND DATE(umc.createdAt) = DATE(:date)")
List<UserMissionChoices> findAllByUserAndCreatedAt(User user, LocalDate date);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public void saveAll(List<UserMissionChoices> missionChoices) {
}

public void deleteByUser(User user) {
userMissionChoicesRepository.deleteByUser(user);
userMissionChoicesRepository.deleteAllByUser(user);
}

public List<UserMissionChoices> getUserMissionChoice(User user) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import sopt.org.motivoo.domain.mission.entity.CompletedStatus;
import sopt.org.motivoo.domain.mission.entity.Mission;
import sopt.org.motivoo.domain.mission.entity.MissionQuest;
import sopt.org.motivoo.domain.mission.entity.UserMission;
Expand All @@ -33,12 +34,12 @@ public interface UserMissionRepository extends JpaRepository<UserMission, Long>


//== DELETE ==//
void deleteByUser(User user);
void deleteAllByUser(User user);


//== UPDATE ==//
@Modifying
@Query("UPDATE UserMission um SET um.mission = :mission, um.missionQuest = :quest WHERE um.user = :user AND DATE(um.createdAt) = DATE(:date)")
void updateValidTodayMission(Mission mission, MissionQuest quest, User user, LocalDate date);
@Query("UPDATE UserMission um SET um.mission = :mission, um.missionQuest = :quest, um.completedStatus = :status WHERE um.user = :user AND DATE(um.createdAt) = DATE(:date)")
void updateValidTodayMission(Mission mission, MissionQuest quest, CompletedStatus status, User user, LocalDate date);
}

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package sopt.org.motivoo.domain.mission.repository;

import static sopt.org.motivoo.domain.mission.entity.CompletedStatus.*;
import static sopt.org.motivoo.domain.mission.exception.MissionExceptionType.*;

import java.time.LocalDate;
Expand Down Expand Up @@ -42,12 +43,12 @@ public boolean existsByUser(User user) {

//== DELETE ==//
public void deleteByUser(User user) {
userMissionRepository.deleteByUser(user);
userMissionRepository.deleteAllByUser(user);
}

//== UPDATE ==//
public void updateUserMission(User user, Mission mission, MissionQuest quest) {
userMissionRepository.updateValidTodayMission(mission, quest, user, LocalDate.now());
userMissionRepository.updateValidTodayMission(mission, quest, IN_PROGRESS, user, LocalDate.now());
}

//== CREATE ==//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
import static sopt.org.motivoo.domain.mission.entity.CompletedStatus.*;
import static sopt.org.motivoo.domain.mission.exception.MissionExceptionType.*;
import static sopt.org.motivoo.domain.user.exception.UserExceptionType.*;
import static sopt.org.motivoo.external.s3.S3BucketDirectory.*;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
Expand Down Expand Up @@ -141,6 +141,7 @@ public List<UserMissionChoices> getFilteredMissionChoices(User user, List<Missio
UserMissionChoices missionChoice = UserMissionChoices.builder()
.mission(missionChoicesFiltered.get(i))
.user(user).build();
missionChoice.setCreatedAtNow(LocalDateTime.now());
missionChoices.add(missionChoice);
}

Expand Down Expand Up @@ -292,7 +293,7 @@ public static void checkedUserMissionEmpty(User user) {
// 매칭된 유저의 탈퇴 여부 검사
public static void checkMatchedUserWithdraw(User opponentUser) {
if (opponentUser.isDeleted()) {
throw new UserException(ALREADY_WITHDRAW_USER);
throw new UserException(ALREADY_WITHDRAW_OPPONENT_USER);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ public Long choiceTodayMission(final TodayMissionChoiceCommand request, final Lo
MissionQuest missionQuest = missionQuestRetriever.getRandomMissionQuest();
userMissionRetriever.updateUserMission(user, mission, missionQuest);
UserMission todayMission = user.getCurrentUserMission();
todayMission.updateCompletedStatus(IN_PROGRESS);
return todayMission.getId();
}

Expand Down Expand Up @@ -188,11 +187,12 @@ public TodayMissionResult getTodayMission(final Long userId) {
*/
boolean existsUserMission = userMissionRetriever.existsByUser(user);
boolean isFiltered = userMissionChoicesRetriever.existsByUser(user);
log.info("User {}의 UserMission이 비어있니? {} ", user.getNickname(), existsUserMission);
log.info("User {}의 UserMission이 존재하니? {} ", user.getNickname(), existsUserMission);
log.info("오늘의 미션 선택지 필터링을 거쳤니? {} ", isFiltered);

// 1) 처음 가입한 유저의 경우 -> 미션 선택지 세팅 완료
if (!existsUserMission) {
log.info("첫 가입 유저 미션 필터링 진입");
log.info("1. 첫 가입 유저 미션 필터링 진입");
createEmptyMission(List.of(user, opponentUser));
return getMissionChoicesResult(user);
}
Expand All @@ -201,6 +201,7 @@ public TodayMissionResult getTodayMission(final Long userId) {

// 2) 필터링 로직을 한 번 이상 거친 경우 -> 저장된 미션 선택지 가져오기
if (isFiltered && todayMission.isEmptyUserMission()) {
log.info("2. 필터링 로직을 한 번 이상 거친 경우");
List<UserMissionChoices> missionChoice = userMissionChoicesRetriever.getUserMissionChoice(user);
return TodayMissionResult.of(missionChoice);
}
Expand All @@ -210,12 +211,7 @@ public TodayMissionResult getTodayMission(final Long userId) {

// 3) 일반적인 경우 - 마션 선택지 필터링
if (todayMission.isNowDate() && todayMission.isEmptyUserMission()) {
log.info("필터링 GO");
return getMissionChoicesResult(user);
}
// 3-1) TODO DB 초기화 하고 삭제해도 됨!
if (!validateTodayDateMission(todayMission)) {
createEmptyMission(List.of(user, opponentUser));
log.info("3. 일반적인 경우(미션 선택지 필터링 이전)");
return getMissionChoicesResult(user);
}

Expand All @@ -232,6 +228,7 @@ private TodayMissionResult getMissionChoicesResult(User user) {

List<UserMissionChoices> todayMissionChoices = filterTodayUserMission(user);
log.info("첫 가입 유저 오늘의 미션 선택지 세팅 완료! : {}", todayMissionChoices.size());
user.addTodayUserMissionChoice(todayMissionChoices);
return TodayMissionResult.of(todayMissionChoices);
}

Expand All @@ -253,7 +250,7 @@ public void createEmptyMission(List<User> users) {
MissionQuest missionQuest = missionQuestRetriever.getRandomMissionQuest();

List<User> filteredUsers = users.stream()
.filter(user -> !user.getUserMissions().isEmpty() && !user.getCurrentUserMission().isNowDate())
.filter(user -> user.getUserMissions().isEmpty() || (!user.getUserMissions().isEmpty() && !user.getCurrentUserMission().isNowDate()))
.toList();
userMissionRetriever.bulkSaveInitUserMission(filteredUsers, LocalDate.now(), emptyMission, missionQuest);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,4 @@

public interface ParentchildRepository extends JpaRepository<Parentchild, Long> {
Optional<Parentchild> findByInviteCode(String inviteCode);
void deleteById(Long id);

}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public OnboardingResult onboardInput(Long userId, OnboardingCommand request){

User user = userRetriever.getUserById(userId);
Health health = parentchildManager.onboardInput(user, request);
// healthRetriever.validateHealthByUser(user); // TODO API 중복 호출 예외처리
healthRetriever.validateHealthByUser(user); // TODO API 중복 호출 예외처리
healthRetriever.save(health);

// Slack 신규 유저 알림 전송
Expand Down Expand Up @@ -82,18 +82,18 @@ public InviteReceiveResult matchRelation(Long userId, InviteCommand request){
checkOnboardingCompleted(user);

Parentchild parentchild = parentchildRetriever.getByInviteCode(request.inviteCode());
int count = userRetriever.getParentchildUserCnt(parentchild);
if (count == 1) {
// validateInviteRequest(user, parentchild);
completeMatching(user, parentchild);
}
Long opponentUserId = userRetriever.getOpponentUserId(userId);
log.info("매칭된 상대 유저의 ID: {}", opponentUserId);

if (parentchild.isMatched()) {
return new InviteReceiveResult(userId, opponentUserId, true);
}
if (user.getParentchild() != null) {
validateInviteRequest(user, parentchild);
}

validateInviteRequest(user, parentchild);
completeMatching(user, parentchild);

return new InviteReceiveResult(userId, opponentUserId, true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ public class User extends BaseTimeEntity {
@OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
private final List<UserMission> userMissions = new ArrayList<>();

// @OneToMany(fetch = FetchType.EAGER)
// private final List<UserMissionChoices> userMissionChoice = new ArrayList<>();
@OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
private final List<UserMissionChoices> userMissionChoice = new ArrayList<>();

@Builder
private User(String nickname, String socialId, SocialPlatform socialPlatform, String socialAccessToken,
Expand Down Expand Up @@ -149,13 +149,10 @@ public void setUserMissionToNull(){
// this.userMissionChoice.clear();
// }

// public void setPreUserMissionChoice(List<UserMissionChoices> userMissionChoice) {
// log.info("임시 UserMission 선택지(매일 자정 초기화 후, 메인 홈 첫 진입 시 업데이트: {}가지 / User-{}가지", userMissionChoice.size(), this.userMissionChoice.size());
// if (this.userMissionChoice.isEmpty()) {
// this.userMissionChoice.addAll(userMissionChoice);
// }
//
// }
public void addTodayUserMissionChoice(List<UserMissionChoices> userMissionChoice) {
log.info("UserMission 선택지(매일 자정 초기화 후, 메인 홈 첫 진입 시 업데이트: {}가지 / User-{}가지", userMissionChoice.size(), this.userMissionChoice.size());
this.userMissionChoice.addAll(userMissionChoice);
}

// 가장 최근의 운동 미션 조회
public UserMission getCurrentUserMission() {
Expand Down
Loading

0 comments on commit fe5ae6e

Please sign in to comment.