Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 구직자 탈퇴 #247

Merged
merged 2 commits into from
Jun 30, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ public ResponseEntity<GlobalApiResponse<Void>> updateUserProfile(
@ApiResponses({
@ApiResponse(responseCode = "200", description = "기업 사용자 전체 조회 성공",
content = @Content(mediaType = "application/json",
examples = @ExampleObject(value = "{ \"message\": \"기업 사용자 전체 조회 성공\", \"data\": { \"cpUsers\": [{ \"cpUserId\": 1, \"cpName\": \"고양이탕후루\", \"reviewCount\": 3, \"averageRating\": 3.0 }], \"totalCpUserCount\": 1, \"currentPage\": 0, \"totalPages\": 1 } }"))),
examples = @ExampleObject(value = "{ \"message\": \"기업 사용자 전체 조회 성공\", \"data\": { \"cpUsers\": [{ \"cpUserId\": 1, \"cpName\": \"고양이탕후루\", \"reviewCount\": 3, \"averageRating\": 3.0 }], \"totalCpUserCount\": 1, \"currentPage\": 0, \"totalPages\": 1, \"viewCount\": null } }"))),
@ApiResponse(responseCode = "400", description = "잘못된 요청 값",
content = @Content(mediaType = "application/json",
examples = @ExampleObject(value = "{\"message\": \"잘못된 요청 값입니다.\" }"))),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ public class CpUserResponseDTO {
private Integer reviewCount;

private Float averageRating;

private Integer viewCount;
}
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,13 @@ public void usePointsForBanner(Long cpUserId) {
}

private CpUserResponseDTO convertToDTO(CompanyUser companyUser) {

return new CpUserResponseDTO(
companyUser.getCpUserId(),
companyUser.getCpName(),
companyUser.getReviewCount(),
companyUser.getAverageRating()
companyUser.getAverageRating(),
companyUser.getViewCount()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ public interface SavedPointRepository extends JpaRepository<SavedPoint, Long>, S
List<SavedPoint> findByPointPointId(Long pointId);

List<SavedPoint> findSavedPointsWithCursor(Long pointId, Long cursorId, int limit);

void deleteByPointPointId(Long pointId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ public interface UsedPointRepository extends JpaRepository<UsedPoint, Long>, Use
List<UsedPoint> findByPointPointId(Long pointId);

List<UsedPoint> findTop3ByUpTypeOrderByCreatedAtDesc(UpType upType, Pageable pageable);

void deleteByPointPointId(Long pointId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,18 @@ public List<UsedPointDetailResponseDTO> getPublicLatestBannerUsage() {
.collect(Collectors.toList());
}

@Transactional
public void deletePointData(Point point) {

if (point != null) {
Long pointId = point.getPointId();
savedPointRepository.deleteByPointPointId(pointId);
usedPointRepository.deleteByPointPointId(pointId);
pointRepository.delete(point);
}
}


/*
공통 로직을 메서드로 추출
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ public interface BoardScrapsRepository extends JpaRepository<BoardScrap, Long> {
boolean existsByUser_UserIdAndBoard_BoardId(Long userId, Long boardId);

void deleteByUser_UserIdAndBoard_BoardId(Long userId, Long boardId);

void deleteByUser_UserId(Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ public interface CpUserScrapRepository extends JpaRepository<CpUserScrap, Long>
void deleteByUser_UserIdAndCompanyUser_CpUserId(Long userId, Long cpUserId);

Page<CpUserScrap> findByUser_UserId(Long userId, Pageable pageable);

void deleteByUser_UserId(Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ public interface JobScrapRepository extends JpaRepository<JobScrap, Long> {
Page<JobScrap> findByUser_UserId(Long userId, Pageable pageable);

void deleteByUser_UserIdAndJobBoard_JobBoardId(Long userId, Long jobBoardId);

void deleteByUser_UserId(Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,14 @@ public CpUserScrapListResponseDTO getAllCpUserScrap(User user, int page, int siz
cpUserScrapPage.getSize()
);
}

@Transactional
public void deleteAllScrapsForUser(User user) {

Long userId = user.getUserId();

boardScrapsRepository.deleteByUser_UserId(userId);
jobScrapRepository.deleteByUser_UserId(userId);
cpUserScrapRepository.deleteByUser_UserId(userId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
import team9502.sinchulgwinong.domain.user.dto.request.UserDeleteRequestDTO;
import team9502.sinchulgwinong.domain.user.dto.request.UserPasswordUpdateRequestDTO;
import team9502.sinchulgwinong.domain.user.dto.request.UserProfileUpdateRequestDTO;
import team9502.sinchulgwinong.domain.user.dto.response.UserProfileResponseDTO;
Expand Down Expand Up @@ -128,4 +129,38 @@ public ResponseEntity<GlobalApiResponse<Void>> updateUserProfile(
SUCCESS_USER_PASSWORD_UPDATED.getMessage(),
null));
}

@DeleteMapping
@Operation(summary = "회원 탈퇴", description = "로그인한 사용자의 회원 탈퇴를 진행합니다. 스크랩, 게시글, 포인트가 삭제됩니다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "회원 탈퇴 성공",
content = @Content(mediaType = "application/json",
examples = @ExampleObject(value = "{\"message\": \"구직자 회원 탈퇴 성공\", \"data\": null }"))),
@ApiResponse(responseCode = "400", description = "요청 처리 중 오류 발생",
content = @Content(mediaType = "application/json",
examples = {
@ExampleObject(name = "비밀번호 불일치",
value = "{\"message\": \"입력한 비밀번호가 기존 비밀번호와 일치하지 않습니다.\", \"data\": null }"),
@ExampleObject(name = "잘못된 사용자 유형",
value = "{\"message\": \"잘못된 사용자 유형입니다.\", \"data\": null }")
})),
@ApiResponse(responseCode = "404", description = "사용자를 찾을 수 없음",
content = @Content(mediaType = "application/json",
examples = @ExampleObject(value = "{\"message\": \"사용자를 찾을 수 없습니다.\", \"data\": null }"))),
@ApiResponse(responseCode = "500", description = "서버 내부 오류",
content = @Content(mediaType = "application/json",
examples = @ExampleObject(value = "{\"message\": \"서버 오류가 발생했습니다.\", \"data\": null }")))
})
public ResponseEntity<GlobalApiResponse<Void>> deleteUser(
@AuthenticationPrincipal UserDetailsImpl userDetails,
@RequestBody UserDeleteRequestDTO requestDTO) {

userService.deleteUser(userDetails.getUserId(), requestDTO);

return ResponseEntity.status(SUCCESS_USER_DELETED.getHttpStatus())
.body(
GlobalApiResponse.of(
SUCCESS_USER_DELETED.getMessage(),
null));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package team9502.sinchulgwinong.domain.user.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class UserDeleteRequestDTO {

@Schema(description = "비밀번호", example = "Password1!")
private String password;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.DynamicUpdate;
import team9502.sinchulgwinong.domain.board.entity.Board;
import team9502.sinchulgwinong.domain.oauth.enums.SocialType;
import team9502.sinchulgwinong.domain.point.CommonPoint;
import team9502.sinchulgwinong.domain.point.entity.Point;
import team9502.sinchulgwinong.global.entity.BaseTimeEntity;

import java.util.List;

@Entity
@Getter
@Builder
Expand Down Expand Up @@ -50,4 +53,7 @@ public class User extends BaseTimeEntity implements CommonPoint {
@Enumerated(EnumType.STRING)
@Column(nullable = false, length = 20)
private SocialType loginType;

@OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Board> boards;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import team9502.sinchulgwinong.domain.board.repository.BoardRepository;
import team9502.sinchulgwinong.domain.email.service.EmailVerificationService;
import team9502.sinchulgwinong.domain.point.service.PointService;
import team9502.sinchulgwinong.domain.scrap.service.ScrapService;
import team9502.sinchulgwinong.domain.user.dto.request.UserDeleteRequestDTO;
import team9502.sinchulgwinong.domain.user.dto.request.UserPasswordUpdateRequestDTO;
import team9502.sinchulgwinong.domain.user.dto.request.UserProfileUpdateRequestDTO;
import team9502.sinchulgwinong.domain.user.dto.response.UserProfileResponseDTO;
Expand All @@ -21,6 +25,9 @@ public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final EmailVerificationService emailVerificationService;
private final BoardRepository boardRepository;
private final PointService pointService;
private final ScrapService scrapService;

@Transactional(readOnly = true)
public UserProfileResponseDTO getUserProfile(Long userId) {
Expand Down Expand Up @@ -92,4 +99,22 @@ public void updateUserPassword(Long userId, UserPasswordUpdateRequestDTO request
userRepository.save(user);
}

@Transactional
public void deleteUser(Long userId, UserDeleteRequestDTO requestDTO) {

User user = userRepository.findById(userId)
.orElseThrow(() -> new ApiException(ErrorCode.USER_NOT_FOUND));

if (!passwordEncoder.matches(requestDTO.getPassword(), user.getPassword())) {
throw new ApiException(ErrorCode.PASSWORD_MISMATCH);
}

boardRepository.deleteAll(user.getBoards());

pointService.deletePointData(user.getPoint());

scrapService.deleteAllScrapsForUser(user);

userRepository.delete(user);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public enum SuccessCode {
SUCCESS_USER_PROFILE_READ(HttpStatus.OK, "구직자 프로필 조회 성공"),
SUCCESS_USER_PROFILE_UPDATED(HttpStatus.OK, "구직자 프로필 수정 성공"),
SUCCESS_USER_PASSWORD_UPDATED(HttpStatus.OK, "구직자 비밀번호 수정 성공"),
SUCCESS_USER_DELETED(HttpStatus.OK, "구직자 회원 탈퇴 성공"),

// SocialLogin
SUCCESS_SOCIAL_LOGIN(HttpStatus.OK, "소셜 로그인 성공"),
Expand Down