-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: 마이페이지를 위한 멤버의 좋아요한 킬링파트 조회 기능 완성
- Loading branch information
1 parent
ea2c4d5
commit 76d87f3
Showing
9 changed files
with
283 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
backend/src/main/java/shook/shook/song/application/KillingPartService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package shook.shook.song.application; | ||
|
||
import java.util.Comparator; | ||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
import shook.shook.auth.ui.argumentresolver.MemberInfo; | ||
import shook.shook.member.domain.Member; | ||
import shook.shook.member.domain.repository.MemberRepository; | ||
import shook.shook.member.exception.MemberException; | ||
import shook.shook.member.exception.MemberException.MemberNotExistException; | ||
import shook.shook.song.application.dto.LikedKillingPartResponse; | ||
import shook.shook.song.domain.killingpart.KillingPart; | ||
import shook.shook.song.domain.killingpart.KillingPartLike; | ||
import shook.shook.song.domain.killingpart.repository.KillingPartLikeRepository; | ||
|
||
@RequiredArgsConstructor | ||
@Transactional(readOnly = true) | ||
@Service | ||
public class KillingPartService { | ||
|
||
private final KillingPartLikeRepository killingPartLikeRepository; | ||
private final MemberRepository memberRepository; | ||
|
||
public List<LikedKillingPartResponse> findLikedKillingPartByMemberId( | ||
final MemberInfo memberInfo | ||
) { | ||
if (memberInfo.getAuthority().isAnonymous()) { | ||
throw new MemberException.MemberNotExistException(); | ||
} | ||
|
||
final Member member = memberRepository.findById(memberInfo.getMemberId()) | ||
.orElseThrow(MemberNotExistException::new); | ||
|
||
final List<KillingPartLike> likes = | ||
killingPartLikeRepository.findAllByMemberAndIsDeleted(member, false); | ||
|
||
return likes.stream() | ||
.sorted(Comparator.comparing(KillingPartLike::getUpdatedAt).reversed()) | ||
.map(killingPartLike -> { | ||
final KillingPart killingPart = killingPartLike.getKillingPart(); | ||
return LikedKillingPartResponse.of(killingPart.getSong(), killingPart); | ||
}).toList(); | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
backend/src/main/java/shook/shook/song/application/dto/LikedKillingPartResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package shook.shook.song.application.dto; | ||
|
||
import lombok.AccessLevel; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import shook.shook.song.domain.Song; | ||
import shook.shook.song.domain.killingpart.KillingPart; | ||
|
||
@AllArgsConstructor(access = AccessLevel.PRIVATE) | ||
@Getter | ||
public class LikedKillingPartResponse { | ||
|
||
private final Long songId; | ||
private final String title; | ||
private final String singer; | ||
private final String albumCoverUrl; | ||
private final Long partId; | ||
private final int start; | ||
private final int end; | ||
|
||
public static LikedKillingPartResponse of(final Song song, final KillingPart killingPart) { | ||
return new LikedKillingPartResponse( | ||
song.getId(), | ||
song.getTitle(), | ||
song.getSinger(), | ||
song.getAlbumCoverUrl(), | ||
killingPart.getId(), | ||
killingPart.getStartSecond(), | ||
killingPart.getEndSecond() | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
backend/src/main/java/shook/shook/song/ui/MyPageController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package shook.shook.song.ui; | ||
|
||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
import shook.shook.auth.ui.argumentresolver.Authenticated; | ||
import shook.shook.auth.ui.argumentresolver.MemberInfo; | ||
import shook.shook.song.application.KillingPartService; | ||
import shook.shook.song.application.dto.LikedKillingPartResponse; | ||
|
||
@RequiredArgsConstructor | ||
@RestController | ||
@RequestMapping("/my-page") | ||
public class MyPageController { | ||
|
||
private final KillingPartService killingPartService; | ||
|
||
@GetMapping | ||
public ResponseEntity<List<LikedKillingPartResponse>> getMemberLikedKillingParts( | ||
@Authenticated MemberInfo memberInfo | ||
) { | ||
return ResponseEntity.ok(killingPartService.findLikedKillingPartByMemberId(memberInfo)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
149 changes: 149 additions & 0 deletions
149
backend/src/test/java/shook/shook/song/ui/MyPageControllerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
package shook.shook.song.ui; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import io.restassured.RestAssured; | ||
import io.restassured.http.ContentType; | ||
import java.util.List; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Nested; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; | ||
import org.springframework.boot.test.web.server.LocalServerPort; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.test.context.jdbc.Sql; | ||
import shook.shook.auth.application.TokenProvider; | ||
import shook.shook.song.application.dto.LikedKillingPartResponse; | ||
import shook.shook.song.application.killingpart.KillingPartLikeService; | ||
import shook.shook.song.application.killingpart.dto.KillingPartLikeRequest; | ||
import shook.shook.song.domain.Song; | ||
import shook.shook.song.domain.killingpart.KillingPart; | ||
import shook.shook.song.domain.killingpart.repository.KillingPartRepository; | ||
import shook.shook.song.domain.repository.SongRepository; | ||
|
||
@Sql("classpath:/killingpart/initialize_killing_part_song.sql") | ||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) | ||
class MyPageControllerTest { | ||
|
||
@LocalServerPort | ||
private int port; | ||
|
||
@BeforeEach | ||
void setUp() { | ||
RestAssured.port = port; | ||
} | ||
|
||
private static final String TOKEN_PREFIX = "Bearer "; | ||
private static final long SAVED_MEMBER_ID = 1L; | ||
private static final String SAVED_MEMBER_NICKNAME = "nickname"; | ||
|
||
@Autowired | ||
private TokenProvider tokenProvider; | ||
|
||
@Autowired | ||
private SongRepository songRepository; | ||
|
||
@Autowired | ||
private KillingPartRepository killingPartRepository; | ||
|
||
@Autowired | ||
private KillingPartLikeService killingPartLikeService; | ||
|
||
@DisplayName("멤버가 좋아요한 킬링파트를 최신순으로 정렬하여 반환한다.") | ||
@Nested | ||
class GetLikedKillingParts { | ||
|
||
@DisplayName("좋요한 킬링파트가 존재할 때. ( 취소한 좋아요가 존재할 때 )") | ||
@Test | ||
void likedKillingPartExistWithOneDeletedLikeExist() { | ||
//given | ||
final String accessToken = tokenProvider.createAccessToken(SAVED_MEMBER_ID, | ||
SAVED_MEMBER_NICKNAME); | ||
|
||
final Song firstSong = songRepository.findById(1L).get(); | ||
final Song secondSong = songRepository.findById(2L).get(); | ||
final Song thirdSong = songRepository.findById(3L).get(); | ||
|
||
final List<KillingPart> firstSongKillingPart = killingPartRepository.findAllBySong( | ||
firstSong); | ||
final List<KillingPart> secondSongKillingPart = killingPartRepository.findAllBySong( | ||
secondSong); | ||
final List<KillingPart> thirdSongKillingPart = killingPartRepository.findAllBySong( | ||
thirdSong); | ||
|
||
final KillingPartLikeRequest likeCreateRequest = new KillingPartLikeRequest(true); | ||
final KillingPartLikeRequest likeDeleteRequest = new KillingPartLikeRequest(false); | ||
|
||
killingPartLikeService.updateLikeStatus( | ||
firstSongKillingPart.get(0).getId(), | ||
1L, | ||
likeCreateRequest | ||
); | ||
killingPartLikeService.updateLikeStatus( | ||
firstSongKillingPart.get(2).getId(), | ||
1L, | ||
likeCreateRequest | ||
); | ||
killingPartLikeService.updateLikeStatus( | ||
firstSongKillingPart.get(2).getId(), | ||
1L, | ||
likeDeleteRequest | ||
); | ||
killingPartLikeService.updateLikeStatus( | ||
secondSongKillingPart.get(0).getId(), | ||
1L, | ||
likeCreateRequest | ||
); | ||
killingPartLikeService.updateLikeStatus( | ||
thirdSongKillingPart.get(0).getId(), | ||
1L, | ||
likeCreateRequest | ||
); | ||
|
||
//when | ||
//then | ||
|
||
final List<LikedKillingPartResponse> expected = List.of( | ||
LikedKillingPartResponse.of(thirdSong, thirdSongKillingPart.get(0)), | ||
LikedKillingPartResponse.of(secondSong, secondSongKillingPart.get(0)), | ||
LikedKillingPartResponse.of(firstSong, firstSongKillingPart.get(0)) | ||
); | ||
|
||
final List<LikedKillingPartResponse> response = RestAssured.given().log().all() | ||
.header(HttpHeaders.AUTHORIZATION, TOKEN_PREFIX + accessToken) | ||
.contentType(ContentType.JSON) | ||
.when().log().all() | ||
.get("/my-page") | ||
.then().log().all() | ||
.statusCode(HttpStatus.OK.value()) | ||
.extract().body().jsonPath().getList(".", LikedKillingPartResponse.class); | ||
|
||
assertThat(response).usingRecursiveComparison().isEqualTo(expected); | ||
} | ||
|
||
@DisplayName("좋아요한 킬링파트가 없을 때") | ||
@Test | ||
void notExist() { | ||
//given | ||
final String accessToken = tokenProvider.createAccessToken(SAVED_MEMBER_ID, | ||
SAVED_MEMBER_NICKNAME); | ||
|
||
//when | ||
//then | ||
final List<LikedKillingPartResponse> response = RestAssured.given().log().all() | ||
.header(HttpHeaders.AUTHORIZATION, TOKEN_PREFIX + accessToken) | ||
.contentType(ContentType.JSON) | ||
.when().log().all() | ||
.get("/my-page") | ||
.then().log().all() | ||
.statusCode(HttpStatus.OK.value()) | ||
.extract().body().jsonPath().getList(".", LikedKillingPartResponse.class); | ||
|
||
assertThat(response).isEmpty(); | ||
} | ||
} | ||
} |