-
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.
- Loading branch information
Showing
24 changed files
with
1,256 additions
and
7 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
119 changes: 119 additions & 0 deletions
119
backend/src/main/java/shook/shook/song/application/ArtistSearchService.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,119 @@ | ||
package shook.shook.song.application; | ||
|
||
import java.util.Comparator; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.stream.Stream; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
import shook.shook.song.application.dto.ArtistResponse; | ||
import shook.shook.song.application.dto.ArtistWithSongSearchResponse; | ||
import shook.shook.song.domain.Artist; | ||
import shook.shook.song.domain.InMemoryArtistSynonyms; | ||
import shook.shook.song.domain.Song; | ||
import shook.shook.song.domain.repository.ArtistRepository; | ||
import shook.shook.song.domain.repository.SongRepository; | ||
import shook.shook.song.domain.repository.dto.SongTotalLikeCountDto; | ||
import shook.shook.song.exception.ArtistException; | ||
|
||
@RequiredArgsConstructor | ||
@Transactional(readOnly = true) | ||
@Service | ||
@Slf4j | ||
public class ArtistSearchService { | ||
|
||
private static final int TOP_SONG_COUNT_OF_ARTIST = 3; | ||
|
||
private final InMemoryArtistSynonyms inMemoryArtistSynonyms; | ||
private final ArtistRepository artistRepository; | ||
private final SongRepository songRepository; | ||
|
||
public List<ArtistResponse> searchArtistsByKeyword(final String keyword) { | ||
final List<Artist> artists = findArtistsStartsWithKeyword(keyword); | ||
|
||
return artists.stream() | ||
.map(ArtistResponse::from) | ||
.toList(); | ||
} | ||
|
||
private List<Artist> findArtistsStartsWithKeyword(final String keyword) { | ||
final List<Artist> artistsFoundByName = inMemoryArtistSynonyms.findAllArtistsNameStartsWith( | ||
keyword); | ||
final List<Artist> artistsFoundBySynonym = inMemoryArtistSynonyms.findAllArtistsHavingSynonymStartsWith( | ||
keyword); | ||
|
||
return removeDuplicateArtistResultAndSortByName(artistsFoundByName, artistsFoundBySynonym); | ||
} | ||
|
||
private List<Artist> removeDuplicateArtistResultAndSortByName(final List<Artist> firstResult, | ||
final List<Artist> secondResult) { | ||
return Stream.concat(firstResult.stream(), secondResult.stream()) | ||
.distinct() | ||
.sorted(Comparator.comparing(Artist::getArtistName)) | ||
.toList(); | ||
} | ||
|
||
public List<ArtistWithSongSearchResponse> searchArtistsAndTopSongsByKeyword( | ||
final String keyword) { | ||
final List<Artist> artists = findArtistsStartsOrEndsWithKeyword(keyword); | ||
|
||
return artists.stream() | ||
.map(artist -> ArtistWithSongSearchResponse.of( | ||
artist, | ||
getSongsOfArtistSortedByLikeCount(artist).size(), | ||
getTopSongsOfArtist(artist)) | ||
) | ||
.toList(); | ||
} | ||
|
||
private List<Artist> findArtistsStartsOrEndsWithKeyword(final String keyword) { | ||
final List<Artist> artistsFoundByName = inMemoryArtistSynonyms.findAllArtistsNameStartsOrEndsWith( | ||
keyword); | ||
final List<Artist> artistsFoundBySynonym = inMemoryArtistSynonyms.findAllArtistsHavingSynonymStartsOrEndsWith( | ||
keyword); | ||
|
||
return removeDuplicateArtistResultAndSortByName(artistsFoundByName, artistsFoundBySynonym); | ||
} | ||
|
||
private List<Song> getTopSongsOfArtist(final Artist artist) { | ||
final List<Song> songs = getSongsOfArtistSortedByLikeCount(artist); | ||
if (songs.size() < TOP_SONG_COUNT_OF_ARTIST) { | ||
return songs; | ||
} | ||
|
||
return songs.subList(0, TOP_SONG_COUNT_OF_ARTIST); | ||
} | ||
|
||
private List<Song> getSongsOfArtistSortedByLikeCount(final Artist artist) { | ||
final List<Song> all = songRepository.findAll(); | ||
log.info("all song: {}", all); | ||
final List<SongTotalLikeCountDto> songsWithTotalLikeCount = songRepository.findAllSongsWithTotalLikeCountByArtist( | ||
artist); | ||
|
||
log.info("found song of artist: {}", songsWithTotalLikeCount); | ||
return songsWithTotalLikeCount.stream() | ||
.sorted(Comparator.comparing(SongTotalLikeCountDto::getTotalLikeCount, | ||
Comparator.reverseOrder()) | ||
.thenComparing(songWithTotalLikeCount -> songWithTotalLikeCount.getSong().getId(), | ||
Comparator.reverseOrder()) | ||
) | ||
.map(SongTotalLikeCountDto::getSong) | ||
.toList(); | ||
} | ||
|
||
public ArtistWithSongSearchResponse searchAllSongsByArtist(final long artistId) { | ||
final Artist artist = findArtistById(artistId); | ||
final List<Song> songs = getSongsOfArtistSortedByLikeCount(artist); | ||
|
||
return ArtistWithSongSearchResponse.of(artist, songs.size(), songs); | ||
} | ||
|
||
private Artist findArtistById(final long artistId) { | ||
return artistRepository.findById(artistId) | ||
.orElseThrow(() -> new ArtistException.NotExistException( | ||
Map.of("ArtistId", String.valueOf(artistId)) | ||
)); | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
backend/src/main/java/shook/shook/song/application/dto/ArtistResponse.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,30 @@ | ||
package shook.shook.song.application.dto; | ||
|
||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import lombok.AccessLevel; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import shook.shook.song.domain.Artist; | ||
|
||
@Schema(description = "아티스트를 통한 아티스트, 해당 아티스트의 노래 검색 결과") | ||
@AllArgsConstructor(access = AccessLevel.PRIVATE) | ||
@Getter | ||
public class ArtistResponse { | ||
|
||
@Schema(description = "아티스트 id", example = "1") | ||
private final Long id; | ||
|
||
@Schema(description = "가수 이름", example = "가수") | ||
private final String singer; | ||
|
||
@Schema(description = "가수 대표 이미지 url", example = "https://image.com/artist-profile.jpg") | ||
private final String profileImageUrl; | ||
|
||
public static ArtistResponse from(final Artist artist) { | ||
return new ArtistResponse( | ||
artist.getId(), | ||
artist.getArtistName(), | ||
artist.getProfileImageUrl() | ||
); | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
backend/src/main/java/shook/shook/song/application/dto/ArtistWithSongSearchResponse.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,47 @@ | ||
package shook.shook.song.application.dto; | ||
|
||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import java.util.List; | ||
import lombok.AccessLevel; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import shook.shook.song.domain.Artist; | ||
import shook.shook.song.domain.Song; | ||
|
||
@Schema(description = "아티스트를 통한 아티스트, 해당 아티스트의 노래 검색 결과") | ||
@AllArgsConstructor(access = AccessLevel.PRIVATE) | ||
@Getter | ||
public class ArtistWithSongSearchResponse { | ||
|
||
@Schema(description = "아티스트 id", example = "1") | ||
private final Long id; | ||
|
||
@Schema(description = "가수 이름", example = "가수") | ||
private final String singer; | ||
|
||
@Schema(description = "가수 대표 이미지 url", example = "https://image.com/artist-profile.jpg") | ||
private final String profileImageUrl; | ||
|
||
@Schema(description = "가수 노래 총 개수", example = "10") | ||
private final int totalSongCount; | ||
|
||
@Schema(description = "아티스트의 노래 목록") | ||
private final List<SongSearchResponse> songs; | ||
|
||
public static ArtistWithSongSearchResponse of(final Artist artist, final int totalSongCount, | ||
final List<Song> songs) { | ||
return new ArtistWithSongSearchResponse( | ||
artist.getId(), | ||
artist.getArtistName(), | ||
artist.getProfileImageUrl(), | ||
totalSongCount, | ||
convertToSongSearchResponse(songs) | ||
); | ||
} | ||
|
||
private static List<SongSearchResponse> convertToSongSearchResponse(final List<Song> songs) { | ||
return songs.stream() | ||
.map(SongSearchResponse::from) | ||
.toList(); | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
backend/src/main/java/shook/shook/song/application/dto/SongSearchResponse.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,30 @@ | ||
package shook.shook.song.application.dto; | ||
|
||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import lombok.AccessLevel; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import shook.shook.song.domain.Song; | ||
|
||
@Schema(description = "검색 결과 (가수, 가수의 노래) 응답") | ||
@AllArgsConstructor(access = AccessLevel.PRIVATE) | ||
@Getter | ||
public class SongSearchResponse { | ||
|
||
@Schema(description = "노래 id", example = "1") | ||
private final Long id; | ||
|
||
@Schema(description = "노래 제목", example = "제목") | ||
private final String title; | ||
|
||
@Schema(description = "노래 앨범 커버 이미지 url", example = "https://image.com/album-cover.jpg") | ||
private final String albumCoverUrl; | ||
|
||
@Schema(description = "노래 비디오 길이", example = "247") | ||
private final int videoLength; | ||
|
||
public static SongSearchResponse from(final Song song) { | ||
return new SongSearchResponse(song.getId(), song.getTitle(), song.getAlbumCoverUrl(), | ||
song.getLength()); | ||
} | ||
} |
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
Oops, something went wrong.