Skip to content

Commit

Permalink
[Feature/issue 154] 유저 탈퇴 로직 구현
Browse files Browse the repository at this point in the history
[Feature/issue 154] 유저 탈퇴 로직 구현
  • Loading branch information
imenuuu authored Nov 13, 2023
2 parents 6901e6e + 0edd382 commit db928ee
Show file tree
Hide file tree
Showing 43 changed files with 652 additions and 32 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/batch_api_ci_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ jobs:
json: ${{ secrets.FCM_JSON }}
dir: "Match-Common/src/main/resources/"

- run: touch ./src/main/resources/AuthKey_HR7JU89RQ6.p8
- run: echo "${{ secrets.AUTH_KEY }}" > Match-Common/src/main/resources/AuthKey_HR7JU89RQ6.p8
- run: cat ./src/main/resources/AuthKey_HR7JU89RQ6.p8

- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
shell: bash
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/dev_api_ci_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ jobs:
json: ${{ secrets.FCM_JSON }}
dir: "Match-Common/src/main/resources/"

- run: touch ./src/main/resources/AuthKey_HR7JU89RQ6.p8
- run: echo "${{ secrets.AUTH_KEY }}" > Match-Common/src/main/resources/AuthKey_HR7JU89RQ6.p8
- run: cat ./src/main/resources/AuthKey_HR7JU89RQ6.p8


- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
shell: bash
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/prod_api_ci_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ jobs:
json: ${{ secrets.FCM_JSON }}
dir: "Match-Common/src/main/resources/"

- run: touch ./src/main/resources/AuthKey_HR7JU89RQ6.p8
- run: echo "${{ secrets.AUTH_KEY }}" > Match-Common/src/main/resources/AuthKey_HR7JU89RQ6.p8
- run: cat ./src/main/resources/AuthKey_HR7JU89RQ6.p8

- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
shell: bash
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ out/
### VS Code ###
.vscode/

Match-Common/src/main/resources/serviceAccountKey.json
Match-Common/src/main/resources/serviceAccountKey.json
Match-Common/src/main/resources/AuthKey_HR7JU89RQ6.p8
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ public void commence(HttpServletRequest request,
errorCode = NOT_EXISTS_USER_HAVE_TOKEN;
setResponse(response, errorCode, request);
return;
case "NotUserActiveException":
errorCode = NOT_USER_ACTIVE;
setResponse(response, errorCode, request);
return;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo


if (StringUtils.hasText(jwt)&& jwtService.validateToken(servletRequest,jwt)) {
Authentication authentication = jwtService.getAuthentication(jwt,servletRequest);
Authentication authentication = jwtService.getAuthentication(jwt, servletRequest);
SecurityContextHolder.getContext().setAuthentication(authentication);
if(authentication !=null) {
logger.info("Security Context '{}' 인증 정보를 저장했습니다, uri: {} method: {}", authentication.getName(), requestURI, httpServletRequest.getMethod());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.example.matchapi.common.security;

import com.example.matchapi.user.dto.UserRes;
import com.example.matchcommon.exception.NotUserActiveException;
import com.example.matchcommon.properties.JwtProperties;
import com.example.matchdomain.redis.entity.AccessToken;
import com.example.matchdomain.redis.entity.RefreshToken;
Expand Down Expand Up @@ -32,6 +33,8 @@
import java.util.NoSuchElementException;
import java.util.Optional;

import static com.example.matchdomain.common.model.Status.INACTIVE;

@RequiredArgsConstructor
@Component
@Slf4j
Expand Down Expand Up @@ -114,13 +117,22 @@ public Authentication getAuthentication(String token, ServletRequest servletRequ
.parseClaimsJws(token);

Long userId=claims.getBody().get("userId",Long.class);
log.info("user find");
Optional<User> users = userAdaptor.findByUserId(userId);
log.info("user find");

if(users.isEmpty()){
throw new NoSuchElementException("NOT EXISTS USER");
}
if(users.get().getStatus().equals(INACTIVE)){
throw new NotUserActiveException("NOT ACTIVE USER");
}

return new UsernamePasswordAuthenticationToken(users.get(),"",users.get().getAuthorities());
}catch(NoSuchElementException e){
servletRequest.setAttribute("exception","NoSuchElementException");
log.info("유저가 존재하지 않습니다.");
}catch (NotUserActiveException e){
servletRequest.setAttribute("exception","NotUserActiveException");
log.info("유저가 비활성 상태입니다.");
}
return null;
}
Expand All @@ -146,9 +158,6 @@ public boolean validateToken(ServletRequest servletRequest, String token) {
return false;
}
}



return true;
} catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
servletRequest.setAttribute("exception","MalformedJwtException");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ protected void configure(HttpSecurity httpSecurity) throws Exception {
.antMatchers("/order").permitAll()
.antMatchers("/order/serverAuth").permitAll()
.antMatchers("/projects").permitAll()
.antMatchers("/projects/**").permitAll()
.antMatchers(HttpMethod.GET, "/projects/{projectId}").permitAll()
.antMatchers("/projects/list").authenticated()
.antMatchers("/").permitAll()
.antMatchers("/serverAuth").permitAll()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,12 @@ public PageResponse<List<DonationRes.BurningFlameDto>> getBurningFlameList(User
return new PageResponse<>(donationUsers.isLast(), donationUsers.getTotalElements(), regularPaymentConverter.convertToBurningFlameList(donationUsers.getContent()));
}

public void deleteRegularPayment(User user) {
List<RegularPayment> regularPayments = regularPaymentRepository.findByUser(user);
for (RegularPayment regularPayment : regularPayments) {
regularPayment.setStatus(ACTIVE);
regularPayment.setRegularPayStatus(USER_CANCEL);
}
regularPaymentAdaptor.saveAll(regularPayments);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import com.example.matchcommon.exception.errorcode.RequestErrorCode;
import com.example.matchdomain.redis.entity.RefreshToken;
import com.example.matchdomain.redis.repository.RefreshTokenRepository;
import com.example.matchdomain.user.entity.enums.SocialType;
import com.example.matchdomain.user.exception.DeleteUserErrorCode;
import com.example.matchdomain.user.exception.ModifyEmailCode;
import com.example.matchdomain.user.exception.ModifyPhoneErrorCode;
import com.example.matchdomain.user.exception.UserAuthErrorCode;
Expand All @@ -22,13 +24,16 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.Valid;
import java.io.IOException;

import static com.example.matchdomain.user.exception.DeleteUserErrorCode.APPLE_USER_NOT_API;
import static com.example.matchdomain.user.exception.UserAuthErrorCode.INVALID_REFRESH_TOKEN;

@RestController
Expand Down Expand Up @@ -186,9 +191,29 @@ public CommonResponse<UserRes.AlarmAgreeList> patchAlarmAgree(@AuthenticationPri
@PostMapping("/apple")
@ApiErrorCodeExample({UserAuthErrorCode.class})
public CommonResponse<String> postAppleUserInfo(@AuthenticationPrincipal User user,
@RequestBody UserReq.AppleUserInfo appleUserInfo){
@Valid @RequestBody UserReq.AppleUserInfo appleUserInfo){
userService.postAppleUserInfo(user, appleUserInfo);
return CommonResponse.onSuccess("성공");
}

@Operation(summary = "02-12 유저 탈퇴 API")
@DeleteMapping("")
@ApiErrorCodeExample({UserAuthErrorCode.class, DeleteUserErrorCode.class})
public CommonResponse<String> deleteUserInfo(@AuthenticationPrincipal User user){
if(user.getSocialType().equals(SocialType.APPLE)){
throw new BadRequestException(APPLE_USER_NOT_API);
}
userService.deleteUserInfo(user);
return CommonResponse.onSuccess("탈퇴 성공");
}

@Operation(summary = "02-13 애플 유저 탈퇴 API")
@DeleteMapping("/apple")
@ApiErrorCodeExample({UserAuthErrorCode.class})
public CommonResponse<String> deleteAppleUserInfo(@AuthenticationPrincipal User user,
@Valid @RequestBody UserReq.AppleCode appleCode){
userService.deleteAppleUserInfo(user, appleCode);
return CommonResponse.onSuccess("탈퇴 성공");
}

}
15 changes: 15 additions & 0 deletions Match-Api/src/main/java/com/example/matchapi/user/dto/UserReq.java
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,25 @@ public static class ModifyEmail {
@AllArgsConstructor
@NoArgsConstructor
public static class AppleUserInfo {
@NotBlank(message = "이름을 입력해주세요")
private String name;

@NotBlank(message = "생일을 입력해주세요")
private LocalDate birthDate;

@NotBlank(message = "전화번호를 입력해주세요")
private String phone;
}


@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class AppleCode {
@Schema(description = "애플 코드 입력", required = true)
@NotBlank(message = "코드를 입력해주세요")
private String code;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.util.Optional;
import java.util.UUID;

import static com.example.matchdomain.common.model.Status.ACTIVE;
import static com.example.matchdomain.user.exception.UserSignUpErrorCode.EXIST_USER_PHONENUMBER;
import static com.example.matchdomain.user.entity.enums.SocialType.*;

Expand Down Expand Up @@ -46,7 +47,7 @@ public LocalDate birthConversion(String birthYear, String birthDay) {

public void checkUserExists(String phoneNumber, SocialType socialType) {
HashMap<String, String> errorType = new HashMap<>();
Optional<User> user = userRepository.findByPhoneNumberAndSocialTypeNot(phoneNumber.replaceAll("\\D+", "").replaceFirst("^82", "0"), socialType);
Optional<User> user = userRepository.findByPhoneNumberAndSocialTypeNotAndStatus(phoneNumber.replaceAll("\\D+", "").replaceFirst("^82", "0"), socialType, ACTIVE);

if (user.isPresent()) {
errorType.put("signUpType", socialTypeConversion(user.get().getSocialType()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import java.util.Optional;

import static com.example.matchcommon.constants.MatchStatic.BEARER;
import static com.example.matchdomain.common.model.Status.ACTIVE;
import static com.example.matchdomain.user.entity.enums.AuthorityEnum.ROLE_ADMIN;
import static com.example.matchdomain.user.entity.enums.SocialType.*;
import static com.example.matchdomain.user.exception.AdminLoginErrorCode.NOT_ADMIN;
Expand Down Expand Up @@ -87,7 +88,7 @@ public UserRes.UserToken kakaoLogIn(UserReq.SocialLoginToken socialLoginToken) {
KakaoUserInfoDto kakaoUserInfoDto = kakaoFeignClient.getInfo(BEARER + socialLoginToken.getAccessToken());

Long userId;
Optional<User> user = userRepository.findBySocialIdAndSocialType(kakaoUserInfoDto.getId(), KAKAO);
Optional<User> user = userRepository.findBySocialIdAndSocialTypeAndStatus(kakaoUserInfoDto.getId(), KAKAO, ACTIVE);
authHelper.checkUserExists(kakaoUserInfoDto.getPhoneNumber(), KAKAO);

//소셜 로그인 정보가 없을 시
Expand Down Expand Up @@ -156,9 +157,10 @@ public UserRes.UserToken getNaverOauthToken(String code) {
public UserRes.UserToken naverLogIn(UserReq.SocialLoginToken socialLoginToken) {
NaverUserInfoDto naverUserInfoDto = naverFeignClient.getInfo(BEARER + socialLoginToken.getAccessToken());
Long userId;

authHelper.checkUserExists(naverUserInfoDto.getMobile(), NAVER);

Optional<User> user = userRepository.findBySocialIdAndSocialType(naverUserInfoDto.getResponse().getId(), NAVER);
Optional<User> user = userRepository.findBySocialIdAndSocialTypeAndStatus(naverUserInfoDto.getResponse().getId(), NAVER, ACTIVE);

if (user.isEmpty()) userId = naverSignUp(naverUserInfoDto);

Expand All @@ -184,15 +186,15 @@ public UserRes.UserToken signUpUser(UserReq.SignUpUser signUpUser) {
}

public void checkUserPhone(UserReq.UserPhone userPhone) {
if(userRepository.existsByPhoneNumber(userPhone.getPhone())) throw new BadRequestException(USERS_EXISTS_PHONE);
if(userRepository.existsByPhoneNumberAndStatus(userPhone.getPhone(),ACTIVE)) throw new BadRequestException(USERS_EXISTS_PHONE);
}

public void checkUserEmail(UserReq.UserEmail userEmail) {
if(userRepository.existsByEmail(userEmail.getEmail())) throw new BadRequestException(USERS_EXISTS_EMAIL);
if(userRepository.existsByEmailAndStatus(userEmail.getEmail(), ACTIVE)) throw new BadRequestException(USERS_EXISTS_EMAIL);
}

public UserRes.UserToken logIn(UserReq.LogIn logIn) {
User user=userRepository.findByUsername(logIn.getEmail()).orElseThrow(() -> new UnauthorizedException(NOT_EXIST_USER));
User user=userRepository.findByUsernameAndStatus(logIn.getEmail(), ACTIVE).orElseThrow(() -> new UnauthorizedException(NOT_EXIST_USER));

if(!passwordEncoder.matches(logIn.getPassword(),user.getPassword())) throw new BadRequestException(NOT_CORRECT_PASSWORD);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package com.example.matchapi.user.service;

import com.example.matchapi.common.model.AlarmType;
import com.example.matchapi.donation.service.DonationService;
import com.example.matchapi.order.dto.OrderRes;
import com.example.matchapi.order.service.OrderService;
import com.example.matchapi.project.converter.ProjectConverter;
import com.example.matchapi.project.helper.ProjectHelper;
import com.example.matchapi.user.converter.UserConverter;
import com.example.matchapi.user.dto.UserReq;
import com.example.matchapi.user.dto.UserRes;
import com.example.matchcommon.annotation.RedissonLock;
import com.example.matchcommon.exception.BadRequestException;
import com.example.matchcommon.reponse.PageResponse;
import com.example.matchdomain.common.model.Status;
import com.example.matchdomain.donation.entity.RegularPayment;
import com.example.matchdomain.donation.repository.DonationUserRepository;
import com.example.matchdomain.donation.repository.RegularPaymentRepository;
import com.example.matchdomain.project.repository.ProjectUserAttentionRepository;
import com.example.matchdomain.user.entity.User;
Expand All @@ -24,6 +24,7 @@
import com.example.matchdomain.user.repository.UserFcmTokenRepository;
import com.example.matchdomain.user.repository.UserRepository;
import com.example.matchinfrastructure.config.s3.S3UploadService;
import com.example.matchinfrastructure.oauth.apple.service.AppleAuthService;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.domain.Page;
Expand All @@ -32,6 +33,7 @@
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import javax.validation.Valid;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;
Expand Down Expand Up @@ -60,6 +62,8 @@ public class UserService {
private final RegularPaymentRepository regularPaymentRepository;
private final S3UploadService s3UploadService;
private final UserFcmTokenRepository userFcmTokenRepository;
private final DonationService donationService;
private final AppleAuthService appleAuthService;

public Optional<User> findUser(long id) {
return userRepository.findById(id);
Expand Down Expand Up @@ -184,7 +188,7 @@ public void modifyPhoneNumber(User user, UserReq.ModifyPhone phone) {
@Transactional
public void modifyEmail(User user, UserReq.ModifyEmail email) {
if(!user.getEmail().equals(email.getOldEmail())) throw new BadRequestException(NOT_CORRECT_EMAIL);
if(userRepository.existsByEmail(email.getNewEmail())) throw new BadRequestException(ModifyEmailCode.USERS_EXISTS_EMAIL);
if(userRepository.existsByEmailAndStatus(email.getNewEmail(), Status.ACTIVE)) throw new BadRequestException(ModifyEmailCode.USERS_EXISTS_EMAIL);
user.setEmail(email.getNewEmail());
userRepository.save(user);
}
Expand Down Expand Up @@ -225,4 +229,17 @@ public void postAppleUserInfo(User user, UserReq.AppleUserInfo appleUserInfo) {

userRepository.save(user);
}

@RedissonLock(LockName = "유저 탈퇴", key = "#user.id")
public void deleteUserInfo(User user) {
user.setStatus(Status.INACTIVE);
donationService.deleteRegularPayment(user);
userRepository.save(user);
}

public void deleteAppleUserInfo(User user, UserReq.AppleCode appleCode) {
appleAuthService.revokeUser(appleCode.getCode());
deleteUserInfo(user);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.example.matchdomain.donation.entity.enums.PayMethod;
import com.example.matchdomain.donation.entity.enums.RegularStatus;
import com.example.matchdomain.donation.entity.flameEnum.FlameImage;
import com.example.matchdomain.donation.entity.flameEnum.FlameType;
import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse;
import lombok.RequiredArgsConstructor;

Expand All @@ -16,8 +17,6 @@
@Converter
@RequiredArgsConstructor
public class OrderConverter {
private final OrderHelper orderHelper;

public DonationUser donationUser(PortOneBillPayResponse response, Long userId, String flameName, String inherenceNumber, Long projectId, Long regularPaymentId) {
return DonationUser.builder()
.userId(userId)
Expand All @@ -32,6 +31,7 @@ public DonationUser donationUser(PortOneBillPayResponse response, Long userId, S
.projectId(projectId)
.regularPaymentId(regularPaymentId)
.flameImage(FlameImage.NORMAL_IMG.getImg())
.flameType(FlameType.NORMAL_FLAME)
.build();
}

Expand Down
Loading

0 comments on commit db928ee

Please sign in to comment.