Skip to content

Commit

Permalink
Merge pull request #37 from kakao-tech-campus-2nd-step3/feature/36-me…
Browse files Browse the repository at this point in the history
…mber-withdraw

feat: 요구 사항 반영 및 회원 탈퇴 API 구현
  • Loading branch information
peeerr authored Oct 3, 2024
2 parents 189f517 + eebc7d9 commit 7a2ebb1
Show file tree
Hide file tree
Showing 16 changed files with 82 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,9 @@ public ResponseEntity<SuccessResponse<OneFriendDiariesResponse>> getOneFriendDia
@GetMapping("/friends")
public ResponseEntity<SuccessResponse<FriendListResponse>> getFriendList(
@RequestParam(required = false) String nickname,
@RequestParam(required = false) String email,
@RequestParam(defaultValue = "0") int key,
@RequestParam(defaultValue = "10") int size) {
FriendListResponse friendList = friendService.getFriendList(nickname, email, key, size);
FriendListResponse friendList = friendService.getFriendList(nickname, key, size);
SuccessResponse<FriendListResponse> response = SuccessResponse.<FriendListResponse>builder()
.code(HttpStatus.OK.value())
.message("success")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.util.StringUtils;
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;
Expand All @@ -32,7 +33,7 @@ public class MemberController {

private final MemberService memberService;

@Operation(summary = "로그인", description = "이메일과 닉네임으로 로그인합니다.")
@Operation(summary = "로그인", description = "회원 번호와 닉네임으로 로그인합니다.")
// @ApiResponses(value = {
// @ApiResponse(responseCode = "200", description = "로그인 성공",
// content = @Content(schema = @Schema(implementation = JwtResponse.class))),
Expand All @@ -49,11 +50,10 @@ public ResponseEntity<SuccessResponse<JwtResponse>> login(@RequestBody MemberLog
@GetMapping
public ResponseEntity<SuccessResponse<MemberSearchResponse>> searchMembers(
@RequestParam(required = false) String nickname,
@RequestParam(required = false) String email,
@RequestParam(required = false) Long key,
@RequestParam(defaultValue = "10") int size) {

MemberSearchResponse response = memberService.searchMembers(nickname, email, key, size);
MemberSearchResponse response = memberService.searchMembers(nickname, key, size);

return ResponseEntity.ok()
.body(SuccessResponse.ok(response));
Expand Down Expand Up @@ -88,6 +88,14 @@ public ResponseEntity<SuccessResponse<Void>> updateMemberInfo(@AuthenticationPri
.body(SuccessResponse.ok());
}

@DeleteMapping
public ResponseEntity<SuccessResponse<Void>> deleteMember(@AuthenticationPrincipal MemberDetails memberDetails) {
memberService.deleteMember(memberDetails.getId());

return ResponseEntity.ok()
.body(SuccessResponse.ok());
}

private void validateProfileUpdate(MultipartFile profileImage, String nickname) {
if (profileImage == null && !StringUtils.hasText(nickname)) {
throw new GlobalException(ErrorCode.INFO_REQUIRED);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
@Getter
public class MemberLoginRequest {

private String email;
private Long number;
private String nickname;

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,5 @@ public class MemberDetailResponse {
private Long id;
private String profileImageUrl;
private String nickname;
private String email;

}
11 changes: 9 additions & 2 deletions src/main/java/com/potatocake/everymoment/entity/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,34 @@
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLRestriction;

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@SQLDelete(sql = "UPDATE member SET deleted = true WHERE id = ?")
@SQLRestriction("deleted = false")
@Entity
public class Member extends BaseTimeEntity {

@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
private Long id;

@Column(nullable = false, unique = true, length = 50)
private String email;
@Column(nullable = false, unique = true)
private Long number;

@Column(nullable = false, length = 50)
private String nickname;

@Lob
private String profileImageUrl;

@Column(nullable = false)
private boolean deleted = false;

public void update(String nickname, String profileImageUrl) {
this.nickname = nickname;
this.profileImageUrl = profileImageUrl;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,4 @@ public class ValidationErrorMessage {

public static final String VALIDATION_ERROR = "입력 데이터의 유효성을 검사하던 중 문제가 발생했습니다.";

public static final String EMAIL_FORMAT_INVALID = "올바른 이메일 형식을 입력해 주세요.";
public static final String EMAIL_NOT_BLANK = "이메일은 필수 입력 값입니다.";
public static final String EMAIL_SIZE_INVALID = "이메일은 50자 이하여야 합니다.";

public static final String NICKNAME_NOT_BLANK = "닉네임을 입력해 주세요.";
public static final String NICKNAME_SIZE_INVALID = "닉네임은 50자 이하여야 합니다.";

public static final String CATEGORY_NAME_NOT_BLANK = "카테고리명을 입력해 주세요.";

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@

public interface MemberRepository extends JpaRepository<Member, Long> {

Optional<Member> findByEmail(String email);
Optional<Member> findByNumber(Long number);

boolean existsByEmail(String email);
boolean existsByNumber(Long number);

Window<Member> findByNicknameContainingAndEmailContaining(String nickname, String email, ScrollPosition position,
Pageable pageable);
Window<Member> findByNicknameContaining(String nickname, ScrollPosition position, Pageable pageable);

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ public class MemberAuthenticationService implements UserDetailsService {
private final MemberRepository memberRepository;

@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
Member member = memberRepository.findByEmail(email)
public UserDetails loadUserByUsername(String number) throws UsernameNotFoundException {
Member member = memberRepository.findByNumber(Long.valueOf(number))
.orElseThrow(() -> new UsernameNotFoundException(ErrorCode.LOGIN_FAILED.getMessage()));

return new MemberDetails(member);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public String getPassword() {

@Override
public String getUsername() {
return member.getEmail();
return String.valueOf(member.getNumber());
}

public Long getId() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,14 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ

MemberLoginRequest loginRequest = getLoginRequest(request);

if (loginRequest.getEmail() != null && loginRequest.getNickname() != null) {
if (loginRequest.getNumber() == null || loginRequest.getNickname() == null) {
throw new AuthenticationServiceException("Invalid login request");
} else {
registerIfNotExists(loginRequest);
}

UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(
loginRequest.getEmail(), DEFAULT_PASSWORD);
String.valueOf(loginRequest.getNumber()), DEFAULT_PASSWORD);

return authenticationManager.authenticate(authRequest);
}
Expand Down Expand Up @@ -117,14 +119,14 @@ private ErrorResponse getErrorResponse(ErrorCode loginFailed) {
}

private void registerIfNotExists(MemberLoginRequest loginRequest) {
if (!memberRepository.existsByEmail(loginRequest.getEmail())) {
if (!memberRepository.existsByNumber(loginRequest.getNumber())) {
memberRepository.save(getMember(loginRequest));
}
}

private Member getMember(MemberLoginRequest loginRequest) {
Member member = Member.builder()
.email(loginRequest.getEmail())
.number(loginRequest.getNumber())
.nickname(loginRequest.getNickname())
.build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ public OneFriendDiariesResponse OneFriendDiariesResponse(Long id, LocalDate date

Pageable pageable = PageRequest.of(key, size);

Page<Diary> diaries = diaryRepository.findAll(DiarySpecification.filterDiaries(null, null, date, null, null, null)
.and((root, query, builder) -> builder.equal(root.get("memberId").get("id"), id)), pageable);
Page<Diary> diaries = diaryRepository.findAll(
DiarySpecification.filterDiaries(null, null, date, null, null, null)
.and((root, query, builder) -> builder.equal(root.get("memberId").get("id"), id)), pageable);

List<FriendDiarySimpleResponse> diaryList = diaries.getContent().stream()
.map(this::convertToFriendDiariesResponseDTO)
Expand All @@ -68,15 +69,15 @@ public OneFriendDiariesResponse OneFriendDiariesResponse(Long id, LocalDate date

//내 친구 목록 조회
@Transactional(readOnly = true)
public FriendListResponse getFriendList(String nickname, String email, int key, int size) {
public FriendListResponse getFriendList(String nickname, int key, int size) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
MemberDetails memberDetails = (MemberDetails) authentication.getPrincipal();
Member currentMember = memberDetails.getMember();
Long memberId = currentMember.getId();

Pageable pageable = PageRequest.of(key, size);

Specification<Friend> spec = FriendSpecification.filterFriends(memberId, nickname, email)
Specification<Friend> spec = FriendSpecification.filterFriends(memberId, nickname)
.and((root, query, builder) -> builder.equal(root.get("memberId").get("id"), memberId));

Page<Friend> friends = friendRepository.findAll(spec, pageable);
Expand Down Expand Up @@ -147,7 +148,7 @@ private FriendDiarySimpleResponse convertToFriendDiariesResponseDTO(Diary savedD
}

//친구 프로필 DTO 변환
private FriendProfileResponse convertToFriendProfileResponseDTO(Friend friend){
private FriendProfileResponse convertToFriendProfileResponseDTO(Friend friend) {
Member friendMember = friend.getFriendId();
return FriendProfileResponse.builder()
.id(friendMember.getId())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import org.springframework.data.jpa.domain.Specification;

public class FriendSpecification {
public static Specification<Friend> filterFriends(Long memberId, String nickname, String email) {
public static Specification<Friend> filterFriends(Long memberId, String nickname) {
return (Root<Friend> root, CriteriaQuery<?> query, CriteriaBuilder builder) -> {
Predicate predicate = builder.conjunction();

Expand All @@ -21,11 +21,6 @@ public static Specification<Friend> filterFriends(Long memberId, String nickname
predicate = builder.and(predicate, builder.like(friendJoin.get("nickname"), "%" + nickname + "%"));
}

if (email != null) {
Join<Friend, Member> friendJoin = root.join("friendId");
predicate = builder.and(predicate, builder.like(friendJoin.get("email"), "%" + email + "%"));
}

return predicate;
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ public class MemberService {
private final S3FileUploader s3FileUploader;

@Transactional(readOnly = true)
public MemberSearchResponse searchMembers(String nickname, String email, Long key, int size) {
Window<Member> window = fetchMemberWindow(nickname, email, key, size);
public MemberSearchResponse searchMembers(String nickname, Long key, int size) {
Window<Member> window = fetchMemberWindow(nickname, key, size);
List<MemberResponse> members = convertToMemberResponses(window.getContent());
Long nextKey = pagingUtil.getNextKey(window, Member::getId);

Expand All @@ -51,7 +51,6 @@ public MemberDetailResponse getMyInfo(Long memberId) {
.id(member.getId())
.profileImageUrl(member.getProfileImageUrl())
.nickname(member.getNickname())
.email(member.getEmail())
.build();
}

Expand All @@ -76,15 +75,20 @@ public void updateMemberInfo(Long id, MultipartFile profileImage, String nicknam
member.update(nickname, profileImageUrl);
}

private Window<Member> fetchMemberWindow(String nickname, String email, Long key, int size) {
public void deleteMember(Long memberId) {
Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new GlobalException(ErrorCode.MEMBER_NOT_FOUND));

memberRepository.delete(member);
}

private Window<Member> fetchMemberWindow(String nickname, Long key, int size) {
ScrollPosition scrollPosition = pagingUtil.createScrollPosition(key);
Pageable pageable = pagingUtil.createPageable(size, ASC);

String searchNickname = (nickname == null) ? "" : nickname;
String searchEmail = (email == null) ? "" : email;

return memberRepository.findByNicknameContainingAndEmailContaining(searchNickname, searchEmail, scrollPosition,
pageable);
return memberRepository.findByNicknameContaining(searchNickname, scrollPosition, pageable);
}

private List<MemberResponse> convertToMemberResponses(List<Member> members) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,15 @@ class MemberControllerTest {
void should_SearchMembers_When_ValidInput() throws Exception {
// given
String nickname = "testUser";
String email = "[email protected]";
Long key = 1L;
int size = 10;
MemberSearchResponse response = MemberSearchResponse.builder().build();

given(memberService.searchMembers(nickname, email, key, size)).willReturn(response);
given(memberService.searchMembers(nickname, key, size)).willReturn(response);

// when
ResultActions result = mockMvc.perform(get("/api/members")
.param("nickname", nickname)
.param("email", email)
.param("key", key.toString())
.param("size", String.valueOf(size)));

Expand All @@ -67,15 +65,15 @@ void should_SearchMembers_When_ValidInput() throws Exception {
.andExpect(jsonPath("$.code").value(200))
.andExpect(jsonPath("$.message").value("success"));

then(memberService).should().searchMembers(nickname, email, key, size);
then(memberService).should().searchMembers(nickname, key, size);
}

@Test
@DisplayName("내 정보 조회가 성공적으로 수행된다.")
void should_ReturnMyInfo_When_ValidMember() throws Exception {
// given
Long memberId = 1L;
MemberDetails memberDetails = createMemberDetails(memberId, "[email protected]", "test");
MemberDetails memberDetails = createMemberDetails(memberId, 1234L, "test");

MemberDetailResponse response = MemberDetailResponse.builder().build();
given(memberService.getMyInfo(memberId)).willReturn(response);
Expand Down Expand Up @@ -120,7 +118,7 @@ void should_ReturnMemberInfo_When_ValidMemberId() throws Exception {
void should_UpdateMemberInfo_When_ValidInput() throws Exception {
// given
Long memberId = 1L;
MemberDetails memberDetails = createMemberDetails(memberId, "[email protected]", "test");
MemberDetails memberDetails = createMemberDetails(memberId, 1234L, "test");

MockMultipartFile profileImage = new MockMultipartFile("profileImage", "image.png", "image/png", new byte[]{});
String nickname = "newNickname";
Expand Down Expand Up @@ -154,16 +152,16 @@ void should_ThrowException_When_ProfileImageAndNicknameAreMissing() throws Excep
then(memberService).shouldHaveNoInteractions();
}

private Member createMember(Long memberId, String email, String nickname) {
private Member createMember(Long memberId, Long number, String nickname) {
return Member.builder()
.id(memberId)
.email(email)
.number(number)
.nickname(nickname)
.build();
}

private MemberDetails createMemberDetails(Long memberId, String email, String nickname) {
Member member = createMember(memberId, email, nickname);
private MemberDetails createMemberDetails(Long memberId, Long number, String nickname) {
Member member = createMember(memberId, number, nickname);
return new MemberDetails(member);
}

Expand Down
Loading

0 comments on commit 7a2ebb1

Please sign in to comment.