Skip to content

Commit

Permalink
Merge branch 'develop' into test-deploy
Browse files Browse the repository at this point in the history
  • Loading branch information
seohyun-lee committed Aug 16, 2024
2 parents 1ea8178 + 29028ea commit 6e06728
Show file tree
Hide file tree
Showing 17 changed files with 140 additions and 181 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import sws.songpin.domain.bookmark.dto.request.BookmarkAddRequestDto;
import sws.songpin.domain.bookmark.dto.request.BookmarkRequestDto;
import sws.songpin.domain.bookmark.service.BookmarkService;

@Tag(name = "Bookmark", description = "Bookmark 관련 API입니다.")
Expand All @@ -17,16 +17,16 @@
public class BookmarkController {
private final BookmarkService bookmarkService;

@Operation(summary = "북마크 생성", description = "플레이리스트에 북마크 생성")
@PostMapping
public ResponseEntity<?> createBookmark(@RequestBody @Valid BookmarkAddRequestDto requestDto){
return ResponseEntity.status(HttpStatus.CREATED).body(bookmarkService.createBookmark(requestDto));
}

@Operation(summary = "북마크 삭제", description = "플레이리스트의 북마크 제거")
@DeleteMapping("/{bookmarkId}")
public ResponseEntity<?> deleteBookmark(@PathVariable("bookmarkId") final Long bookmarkId){
bookmarkService.deleteBookmark(bookmarkId);
return ResponseEntity.ok().build();
@Operation(summary = "북마크 상태 변경", description = "북마크가 없으면 생성하고, 있으면 삭제")
@PutMapping
public ResponseEntity<?> changeBookmark(@RequestBody @Valid BookmarkRequestDto requestDto){
boolean isCreated = bookmarkService.changeBookmark(requestDto);
if(isCreated){
// 북마크 생성
return ResponseEntity.status(HttpStatus.CREATED).build();
} else{
// 북마크 삭제
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import sws.songpin.domain.member.entity.Member;
import sws.songpin.domain.playlist.entity.Playlist;

public record BookmarkAddRequestDto(
public record BookmarkRequestDto(
@NotNull Long playlistId
){

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sws.songpin.domain.bookmark.dto.request.BookmarkAddRequestDto;
import sws.songpin.domain.bookmark.dto.response.BookmarkAddResponseDto;
import sws.songpin.domain.bookmark.dto.request.BookmarkRequestDto;
import sws.songpin.domain.bookmark.entity.Bookmark;
import sws.songpin.domain.bookmark.repository.BookmarkRepository;
import sws.songpin.domain.member.entity.Member;
Expand All @@ -31,29 +30,26 @@ public class BookmarkService {
private final MemberService memberService;
private final PlaylistService playlistService;

// 북마크 생성
public BookmarkAddResponseDto createBookmark(BookmarkAddRequestDto requestDto) {
// 북마크 상태 변경
public boolean changeBookmark(BookmarkRequestDto requestDto) {
Member member = memberService.getCurrentMember();
Playlist playlist = playlistService.findPlaylistById(requestDto.playlistId());
if (!member.equals(playlist.getCreator()) && playlist.getVisibility() == Visibility.PRIVATE) {
throw new CustomException(ErrorCode.UNAUTHORIZED_REQUEST);
}
getBookmarkByPlaylistAndMember(playlist, member).ifPresent(bookmark -> {
throw new CustomException(ErrorCode.BOOKMARK_ALREADY_EXISTS);
});
Bookmark bookmark = requestDto.toEntity(member, playlist);
Bookmark savedBookmark = bookmarkRepository.save(bookmark);
return BookmarkAddResponseDto.from(savedBookmark);
}

// 북마크 삭제
public void deleteBookmark(Long bookmarkId) {
Bookmark bookmark = findBookmarkById(bookmarkId);
Member currentMember = memberService.getCurrentMember();
if (!bookmark.getMember().equals(currentMember)) {
throw new CustomException(ErrorCode.UNAUTHORIZED_REQUEST);
Optional<Bookmark> bookmark = getBookmarkByPlaylistAndMember(playlist, member);
if(bookmark.isPresent()){
bookmark.ifPresent(bookmarkRepository::delete);
return false;
}
else{
Bookmark newBookmark = Bookmark.builder()
.member(member)
.playlist(playlist)
.build();
bookmarkRepository.save(newBookmark);
return true;
}
bookmarkRepository.delete(bookmark);
}

// 내 모든 북마크된 플레이리스트 조회
Expand All @@ -74,12 +70,6 @@ public Optional<Bookmark> getBookmarkByPlaylistAndMember(Playlist playlist, Memb
return bookmarkRepository.findByPlaylistAndMember(playlist, member);
}

@Transactional(readOnly = true)
public Bookmark findBookmarkById(Long bookmarkId) {
return bookmarkRepository.findById(bookmarkId)
.orElseThrow(() -> new CustomException(ErrorCode.BOOKMARK_NOT_FOUND));
}

public void deleteAllBookmarksOfMember(Member member){
bookmarkRepository.deleteAllByMember(member);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import sws.songpin.domain.follow.dto.request.FollowAddRequestDto;
import sws.songpin.domain.follow.dto.request.FollowRequestDto;
import sws.songpin.domain.follow.service.FollowService;

@Tag(name = "Follow", description = "Follow 관련 API입니다.")
Expand All @@ -16,16 +17,21 @@
public class FollowController {
private final FollowService followService;

@Operation(summary = "타 유저를 팔로잉", description = "다른 유저를 팔로잉합니다.")
@PostMapping
public ResponseEntity<?> followAdd(@RequestBody @Valid FollowAddRequestDto followAddRequestDto) {
return ResponseEntity.ok(followService.addFollow(followAddRequestDto));
@Operation(summary = "팔로잉 상태 변경", description = "다른 유저를 팔로잉 또는 팔로우 취소합니다.")
@PutMapping
public ResponseEntity<?> changeFollow(@RequestBody @Valid FollowRequestDto requestDto) {
boolean isCreated = followService.createOrDeleteFollow(requestDto);
if (isCreated) {
return ResponseEntity.status(HttpStatus.CREATED).build();
} else {
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
}

@Operation(summary = "팔로잉 취소 또는 팔로워 삭제", description = "나의 팔로잉을 취소하거나 팔로워를 삭제합니다.")
@DeleteMapping("/{followId}")
public ResponseEntity<?> followRemove(@PathVariable final Long followId) {
followService.deleteFollow(followId);
return ResponseEntity.noContent().build();
@Operation(summary = "팔로워 삭제", description = "나의 팔로워를 삭제합니다.")
@DeleteMapping("/followers")
public ResponseEntity<?> deleteFollower(@RequestBody @Valid FollowRequestDto requestDto) {
followService.deleteFollower(requestDto);
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import sws.songpin.domain.follow.entity.Follow;
import sws.songpin.domain.member.entity.Member;

public record FollowAddRequestDto(
@NotNull Long targetMemberId
public record FollowRequestDto(
@NotNull Long memberId
){
public static Follow toEntity(Member follower, Member following) {
return Follow.builder()
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,20 @@
import sws.songpin.domain.member.entity.Member;
import sws.songpin.domain.member.entity.ProfileImg;

public record FollowDto( // FollowerListResponseDto, FollowingListResponseDto에서 사용하는 레코드
public record FollowDto(
Long memberId,
ProfileImg profileImg,
String nickname,
String handle,
Boolean isFollowing,
Long followId
Boolean isFollowing
) {
public static FollowDto from (Member member, Boolean isFollowing, Long followId) {
public static FollowDto from (Member member, Boolean isFollowing) {
return new FollowDto(
member.getMemberId(),
member.getProfileImg(),
member.getNickname(),
member.getHandle(),
isFollowing,
followId // currentMember가 팔로잉하는 경우 currentMember와의 followId 삽입
isFollowing
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import java.util.Optional;

public interface FollowRepository extends JpaRepository<Follow,Long> {
Boolean existsByFollowerAndFollowing(Member follower, Member following);
List<Follow> findAllByFollowing(Member following);
List<Follow> findAllByFollower(Member follower);
Long countByFollowing(Member following);
Expand Down
99 changes: 54 additions & 45 deletions src/main/java/sws/songpin/domain/follow/service/FollowService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,20 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sws.songpin.domain.alarm.service.AlarmService;
import sws.songpin.domain.follow.dto.request.FollowAddRequestDto;
import sws.songpin.domain.follow.dto.response.FollowAddResponseDto;
import sws.songpin.domain.follow.dto.request.FollowRequestDto;
import sws.songpin.domain.follow.dto.response.FollowDto;
import sws.songpin.domain.follow.dto.response.FollowListResponseDto;
import sws.songpin.domain.follow.entity.Follow;
import sws.songpin.domain.follow.repository.FollowRepository;
import sws.songpin.domain.member.entity.Member;
import sws.songpin.domain.member.entity.Status;
import sws.songpin.domain.member.service.MemberService;
import sws.songpin.global.exception.CustomException;
import sws.songpin.global.exception.ErrorCode;

import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
Expand All @@ -29,32 +28,61 @@ public class FollowService {
private final MemberService memberService;
private final AlarmService alarmService;

// 팔로우 추가
public FollowAddResponseDto addFollow(FollowAddRequestDto followAddRequestDto){
Member follower = memberService.getCurrentMember();
Member following = memberService.getActiveMemberById(followAddRequestDto.targetMemberId());
public boolean createOrDeleteFollow(FollowRequestDto requestDto) {
Member currentMember = memberService.getCurrentMember();
Member targetMember = memberService.getActiveMemberById(requestDto.memberId());

if (!follower.equals(memberService.getCurrentMember())){ // follower가 자신이어야 함
throw new CustomException(ErrorCode.UNAUTHORIZED_REQUEST);
} else if (follower.equals(following)){ // follower가 following과 동일하면 팔로우 불가
if (currentMember.equals(targetMember)) { // follower가 following과 동일하면 팔로우 불가
throw new CustomException(ErrorCode.FOLLOW_BAD_REQUEST);
} else if (checkFollowExists(follower, following)){ // follow가 이미 존재하면 팔로우 불가
throw new CustomException(ErrorCode.FOLLOW_ALREADY_EXISTS);
}
Follow follow = followRepository.save(FollowAddRequestDto.toEntity(follower, following));
alarmService.createFollowAlarm(follower, following);
return FollowAddResponseDto.from(follow);

Optional<Follow> followOptional = followRepository.findByFollowerAndFollowing(currentMember, targetMember);
if (checkIfFollowExists(targetMember, currentMember)) { // 팔로우가 존재하면 삭제
followRepository.delete(followOptional.get());
return false;
} else { // 팔로우 추가
followRepository.save(FollowRequestDto.toEntity(currentMember, targetMember));
alarmService.createFollowAlarm(currentMember, targetMember);
return true;
}
}

public void deleteFollower(FollowRequestDto requestDto){
Member currentMember = memberService.getCurrentMember();
Member targetMember = memberService.getActiveMemberById(requestDto.memberId());

if (currentMember.equals(targetMember)) {
throw new CustomException(ErrorCode.FOLLOW_BAD_REQUEST);
}

Optional<Follow> followOptional = followRepository.findByFollowerAndFollowing(targetMember, currentMember);
if (followOptional.isPresent()) { // 팔로우가 존재하면 삭제
followRepository.delete(followOptional.get());
} else {
throw new CustomException(ErrorCode.FOLLOW_NOT_FOUND);
}
}

public Boolean checkIfFollowing(Member targetMember){
Member currentMember = memberService.getCurrentMember();
return checkIfFollowExists(currentMember, targetMember);
}

// 팔로우 삭제
public void deleteFollow(Long followId) {
Follow follow = findFollowById(followId);
public Boolean checkIfFollower(Member targetMember) {
Member currentMember = memberService.getCurrentMember();
boolean isFollowerOrFollowing = currentMember.equals(follow.getFollower()) || currentMember.equals(follow.getFollowing());
if (!isFollowerOrFollowing) { // 자신이 follower거나 following이 아니면 follow 삭제 불가능
throw new CustomException(ErrorCode.UNAUTHORIZED_REQUEST);
return checkIfFollowExists(targetMember, currentMember);
}

public Boolean checkIfFollowExists(Member follower, Member following) {
if (follower.equals(following)) {
return null;
}
Optional<Follow> followOptional = followRepository.findByFollowerAndFollowing(follower, following);
if (followOptional.isPresent()) {
return true;
} else {
return false;
}
followRepository.delete(follow);
}

// 특정 사용자의 팔로잉/팔로워 목록 조회
Expand All @@ -67,14 +95,12 @@ public FollowListResponseDto getFollowList(Long memberId, boolean isFollowingLis
List<FollowDto> followDtoList = followList.stream()
.map(follow -> {
Member member = isFollowingList ? follow.getFollowing() : follow.getFollower();
Long followId = currentMemberFollowingCache.get(member);
// isFollowing: 로그인한 사용자의 member 팔로잉 여부 (null: 자신)
Boolean isFollowing = member.equals(currentMember) ? null : followId != null;
return FollowDto.from(member, isFollowing, followId);
Boolean isFollowing = member.equals(currentMember) ? null : currentMemberFollowingCache.get(member) != null;
return FollowDto.from(member, isFollowing);
})
// 우선순위대로 정렬 (1차: null > true > false, 2차: followId 높은 것부터)
.sorted(Comparator.comparing(FollowDto::isFollowing, Comparator.nullsFirst(Comparator.reverseOrder()))
.thenComparing(FollowDto::followId, Comparator.nullsLast(Comparator.reverseOrder())))
// 우선순위대로 정렬 (null > true > false)
.sorted(Comparator.comparing(FollowDto::isFollowing, Comparator.nullsFirst(Comparator.reverseOrder())))
.collect(Collectors.toList());
return FollowListResponseDto.from(targetMember.equals(currentMember), targetMember.getHandle(), followDtoList);
}
Expand All @@ -86,17 +112,6 @@ public Map<Member, Long> getMemberFollowingCache(Member member) {
.collect(Collectors.toMap(Follow::getFollowing, Follow::getFollowId));
}

@Transactional(readOnly = true)
public Follow findFollowById(Long followId){
return followRepository.findById(followId)
.orElseThrow(() -> new CustomException(ErrorCode.FOLLOW_NOT_FOUND));
}

@Transactional(readOnly = true)
public boolean checkFollowExists(Member follower, Member following) {
return followRepository.existsByFollowerAndFollowing(follower, following).booleanValue();
}

// member의 팔로워들
@Transactional(readOnly = true)
public List<Follow> findAllFollowersOfMember(Member member){
Expand All @@ -119,12 +134,6 @@ public long getFollowingCount(Member member){
return followRepository.countByFollower(member);
}

@Transactional(readOnly = true)
public Long getFollowId(Member follower, Member following){
return followRepository.findByFollowerAndFollowing(follower,following)
.orElseThrow(()-> new CustomException(ErrorCode.FOLLOW_NOT_FOUND)).getFollowId();
}

//회원의 팔로워 및 팔로잉 모두 삭제
public void deleteAllFollowsOfMember(Member member){
followRepository.deleteAllByFollower(member);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,6 @@ public ResponseEntity<?> login(@Valid @RequestBody LoginRequestDto requestDto, H
return ResponseEntity.ok(new LoginResponseDto(tokenDto.accessToken()));
}

@Operation(summary = "로그아웃", description = "Redis와 쿠키에 저장되었던 회원의 Refresh Token을 삭제합니다.")
@PostMapping("/logout")
public ResponseEntity<?> logout(HttpServletResponse response){

return ResponseEntity.ok().build();
}

@Operation(summary = "토큰 재발급", description = "Access Token 만료 시 Refresh Token 을 이용하여 Access Token 및 Refresh Token 재발급")
@PostMapping("/token")
public ResponseEntity<?> reissue(HttpServletRequest request, HttpServletResponse response){
Expand Down
Loading

0 comments on commit 6e06728

Please sign in to comment.