Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion server-config
12 changes: 2 additions & 10 deletions src/main/java/com/chooz/auth/application/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,8 @@
import com.chooz.auth.domain.SocialAccount;
import com.chooz.auth.domain.SocialAccountRepository;
import com.chooz.auth.presentation.dto.TokenResponse;
import com.chooz.common.exception.BadRequestException;
import com.chooz.common.exception.ErrorCode;
import com.chooz.user.application.UserService;
import com.chooz.user.domain.Role;
import com.chooz.user.domain.User;
import com.chooz.user.domain.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
Expand All @@ -29,7 +25,7 @@ public class AuthService {
private final OAuthService oAuthService;
private final SocialAccountRepository socialAccountRepository;
private final UserService userService;
private final UserRepository userRepository;
private final WithdrawHandler withdrawHandler;

public TokenResponse oauthSignIn(String code, String redirectUri) {
OAuthUserInfo oAuthUserInfo = oAuthService.getUserInfo(code, redirectUri);
Expand Down Expand Up @@ -62,11 +58,7 @@ public void signOut(Long userId) {
jwtService.removeToken(userId);
}

@Transactional
public void withdraw(Long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new BadRequestException(ErrorCode.USER_NOT_FOUND));
jwtService.removeToken(userId);
userRepository.delete(user);
withdrawHandler.withdraw(userId);
}
}
48 changes: 48 additions & 0 deletions src/main/java/com/chooz/auth/application/WithdrawHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.chooz.auth.application;

import com.chooz.auth.application.jwt.JwtService;
import com.chooz.auth.application.oauth.OAuthService;
import com.chooz.auth.domain.SocialAccount;
import com.chooz.auth.domain.SocialAccountRepository;
import com.chooz.common.exception.BadRequestException;
import com.chooz.common.exception.ErrorCode;
import com.chooz.image.application.S3Client;
import com.chooz.notification.domain.NotificationRepository;
import com.chooz.post.application.PostCommandService;
import com.chooz.post.persistence.PostJpaRepository;
import com.chooz.user.domain.User;
import com.chooz.user.domain.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class WithdrawHandler {

private final UserRepository userRepository;
private final JwtService jwtService;
private final SocialAccountRepository socialAccountRepository;
private final PostJpaRepository postRepository;
private final NotificationRepository notificationRepository;
private final PostCommandService postCommandService;
private final OAuthService oAuthService;
private final S3Client s3Client;

public void withdraw(Long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new BadRequestException(ErrorCode.USER_NOT_FOUND));
jwtService.removeToken(userId);
notificationRepository.deleteAllByUserId(userId);
postRepository.findAllByUserId(userId)
.forEach(post -> postCommandService.delete(userId, post.getId()));

socialAccountRepository.findByUserId(userId).ifPresent(socialAccount -> {
socialAccountRepository.deleteByUserId(userId);
oAuthService.withdraw(socialAccount.getSocialId());
});
if (!User.DEFAULT_PROFILE_URL.equals(user.getProfileUrl())) {
s3Client.deleteImage(user.getProfileUrl());
}
userRepository.delete(user);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.chooz.auth.application.oauth;

import com.chooz.auth.application.oauth.dto.KakaoAuthResponse;
import com.chooz.auth.application.oauth.dto.KakaoUnlinkResponse;
import com.chooz.auth.application.oauth.dto.KakaoUserInfoResponse;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestHeader;
Expand All @@ -18,4 +19,10 @@ public interface KakaoOAuthClient {

@GetExchange("https://kapi.kakao.com/v2/user/me")
KakaoUserInfoResponse fetchUserInfo(@RequestHeader(name = AUTHORIZATION) String bearerToken);

@PostExchange(url = "https://kapi.kakao.com/v1/user/unlink", contentType = APPLICATION_FORM_URLENCODED_VALUE)
KakaoUnlinkResponse unlink(
@RequestHeader(name = AUTHORIZATION) String bearerToken,
@RequestParam("params") MultiValueMap<String, String> params
);
}
19 changes: 19 additions & 0 deletions src/main/java/com/chooz/auth/application/oauth/OAuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
public class OAuthService {

private static final String BEARER = "Bearer ";
private static final String KAKAO_AK = "KakaoAK ";

private final KakaoOAuthConfig kakaoOAuthConfig;
private final KakaoOAuthClient kakaoOAuthClient;
Expand All @@ -43,4 +44,22 @@ private MultiValueMap<String, String> tokenRequestParams(String authCode, String
params.add("client_secret", kakaoOAuthConfig.clientSecret());
return params;
}

public void withdraw(String socialId) {
try {
kakaoOAuthClient.unlink(
KAKAO_AK + kakaoOAuthConfig.adminKey(),
unlinkRequestParams(socialId)
);
} catch (Exception e) {
log.warn("회원 탈퇴 실패", e);
}
}

private MultiValueMap<String, String> unlinkRequestParams(String socialId) {
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("target_id_type", "user_id");
params.add("target_id", socialId);
return params;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.chooz.auth.application.oauth.dto;

public record KakaoUnlinkResponse(String id) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository
public interface SocialAccountRepository extends JpaRepository<SocialAccount, Long> {

Optional<SocialAccount> findBySocialIdAndProvider(String socialId, Provider provider);

void deleteByUserId(Long userId);

Optional<SocialAccount> findByUserId(Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,9 @@ public void deleteComment(Long postId, Long commentId, Long userId) {
commentRepository.delete(comment);
eventPublisher.publish(DeleteEvent.of(comment.getId(), comment.getClass().getSimpleName().toUpperCase()));
}

public void deleteComments(Long postId) {
commentLikeCommandService.deleteCommentLikeByCommentId(postId);
commentRepository.deleteAllByPostId(postId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ Slice<Comment> findByPostId(

List<Comment> findByPostIdAndDeletedFalse(@NotNull Long postId);

void deleteAllByPostId(Long postId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public record KakaoOAuthConfig(
String authorizationUri,
String clientId,
String clientSecret,
String adminKey,
String[] scope,
String userInfoUri
) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/chooz/common/dev/DataInitConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class DataInitConfig {

private final DataInitializer dataInitializer;

@PostConstruct
// @PostConstruct
public void init() {
dataInitializer.init();
}
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/chooz/image/application/ImageService.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@ private String getSignedGetUrl(String filePath) {
URI domain = URI.create(imageProperties.endpoint());
return domain.resolve(filePath).toString();
}

public void deleteImage(String assetUrl) {
s3Client.deleteImage(assetUrl);
}
}
3 changes: 2 additions & 1 deletion src/main/java/com/chooz/image/application/S3Client.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.chooz.image.application;

import com.chooz.image.application.dto.PresignedUrlRequestDto;
import com.chooz.image.presentation.dto.PresignedUrlRequest;

public interface S3Client {
String getPresignedPutUrl(PresignedUrlRequestDto presignedUrlRequestDto);

void deleteImage(String assetUrl);
}
15 changes: 14 additions & 1 deletion src/main/java/com/chooz/image/infrastructure/AwsS3Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.chooz.image.application.dto.PresignedUrlRequestDto;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;
Expand All @@ -17,13 +18,16 @@ public class AwsS3Client implements S3Client {

private final String bucket;
private final S3Presigner s3Presigner;
private final software.amazon.awssdk.services.s3.S3Client s3Client;

public AwsS3Client(
@Value("${spring.cloud.aws.s3.bucket}") String bucket,
S3Presigner s3Presigner
S3Presigner s3Presigner,
software.amazon.awssdk.services.s3.S3Client s3Client
) {
this.bucket = bucket;
this.s3Presigner = s3Presigner;
this.s3Client = s3Client;
}

@Override
Expand All @@ -45,4 +49,13 @@ private PutObjectPresignRequest buildPresignedRequest(PresignedUrlRequestDto dto
.putObjectRequest(requestBuilder.build())
.build();
}

@Override
public void deleteImage(String assetUrl) {
DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder()
.bucket(bucket)
.key(assetUrl)
.build();
s3Client.deleteObject(deleteObjectRequest);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ public interface NotificationRepository {
Optional<Notification> findNotificationById(Long id);
boolean existsByReceiverIdAndIsReadFalseAndDeletedFalse(Long userId);
List<Notification> findByTargetIdAndType(Long targetId, TargetType targetType);

void deleteAllByUserId(Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ public interface NotificationJpaRepository extends JpaRepository<Notification, L
"""
)
List<Notification> findByTargetIdAndType(@Param("targetId") Long targetId, @Param("targetType") TargetType targetType);

void deleteAllByReceiverId(Long receiverId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,9 @@ public boolean existsByReceiverIdAndIsReadFalseAndDeletedFalse(Long userId) {
public List<Notification> findByTargetIdAndType(Long targetId, TargetType targetType) {
return notificationJpaRepository.findByTargetIdAndType(targetId, targetType);
}

@Override
public void deleteAllByUserId(Long userId) {
notificationJpaRepository.deleteAllByReceiverId(userId);
}
}
24 changes: 11 additions & 13 deletions src/main/java/com/chooz/post/application/PostCommandService.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
package com.chooz.post.application;

import com.chooz.comment.application.CommentCommandService;
import com.chooz.common.event.DeleteEvent;
import com.chooz.common.event.EventPublisher;
import com.chooz.common.exception.BadRequestException;
import com.chooz.common.exception.ErrorCode;
import com.chooz.image.application.ImageService;
import com.chooz.post.application.dto.PostClosedNotificationEvent;
import com.chooz.post.domain.CloseOption;
import com.chooz.post.domain.PollChoice;
import com.chooz.post.domain.PollOption;
import com.chooz.post.domain.Post;
import com.chooz.post.domain.PollChoice;
import com.chooz.post.domain.PostRepository;
import com.chooz.post.presentation.dto.CreatePostRequest;
import com.chooz.post.presentation.dto.CreatePostResponse;
import com.chooz.post.presentation.dto.UpdatePostRequest;
import com.chooz.thumbnail.domain.Thumbnail;
import com.chooz.thumbnail.domain.ThumbnailRepository;
import com.chooz.vote.application.VoteService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -30,13 +31,14 @@ public class PostCommandService {

private final PostRepository postRepository;
private final ShareUrlService shareUrlService;
private final ThumbnailRepository thumbnailRepository;
private final PostValidator postValidator;
private final CommentCommandService commentCommandService;
private final VoteService voteService;
private final EventPublisher eventPublisher;
private final ImageService imageService;

public CreatePostResponse create(Long userId, CreatePostRequest request) {
Post post = createPost(userId, request);
savePostThumbnail(post);
return new CreatePostResponse(post.getId(), post.getShareUrl());
}

Expand Down Expand Up @@ -73,18 +75,14 @@ private List<PollChoice> createPollChoices(CreatePostRequest request) {
.collect(Collectors.toList());
}

private void savePostThumbnail(Post post) {
PollChoice thumbnailPollChoice = post.getPollChoices().getFirst();
thumbnailRepository.save(
Thumbnail.create(post.getId(), thumbnailPollChoice.getId(), thumbnailPollChoice.getImageUrl())
);
}

@Transactional
public void delete(Long userId, Long postId) {
Post post = postRepository.findById(postId)
.orElseThrow(() -> new BadRequestException(ErrorCode.POST_NOT_FOUND));
post.delete(userId);
voteService.delete(postId);
commentCommandService.deleteComments(postId);
imageService.deleteImage(post.getImageUrl());
postRepository.delete(postId);
eventPublisher.publish(DeleteEvent.of(post.getId(), post.getClass().getSimpleName().toUpperCase()));
}

Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/chooz/post/domain/PostRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,8 @@ public interface PostRepository {
Slice<PostWithVoteCount> findVotedPostsWithVoteCount(Long userId, Long authorId, Long postId, Pageable pageable);

Optional<Post> findByIdAndUserId(Long postId, Long userId);

void deleteAllByUserId(Long userId);

void delete(Long postId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,8 @@ public interface PostJpaRepository extends JpaRepository<Post, Long> {
Optional<CommentActive> findCommentActiveByPostId(@Param("postId") Long postId);

Optional<Post> findByIdAndUserIdAndDeletedFalse(Long postId, Long userId);

void deleteAllByUserId(Long userId);

List<Post> findAllByUserId(Long userId);
}
12 changes: 11 additions & 1 deletion src/main/java/com/chooz/post/persistence/PostRepositoryImpl.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.chooz.post.persistence;

import com.chooz.post.application.dto.FeedDto;
import com.chooz.post.application.dto.PostWithVoteCount;
import com.chooz.post.domain.CommentActive;
import com.chooz.post.domain.Post;
import com.chooz.post.domain.PostRepository;
import com.chooz.post.application.dto.FeedDto;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
Expand Down Expand Up @@ -79,4 +79,14 @@ public Slice<PostWithVoteCount> findVotedPostsWithVoteCount(Long userId, Long au
public Optional<Post> findByIdAndUserId(Long postId, Long userId) {
return postJpaRepository.findByIdAndUserIdAndDeletedFalse(postId, userId);
}

@Override
public void deleteAllByUserId(Long userId) {
postJpaRepository.deleteAllByUserId(userId);
}

@Override
public void delete(Long postId) {
postJpaRepository.deleteById(postId);
}
}
Loading