From 9692bd61c5434f784265b7d25f9cdd9b2f08791c Mon Sep 17 00:00:00 2001 From: siyeonSon Date: Sun, 8 Sep 2024 14:10:39 +0900 Subject: [PATCH 01/16] :heavy_plus_sign: config(api): add spring cloud dependency --- backend/streetdrop-api/build.gradle | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/backend/streetdrop-api/build.gradle b/backend/streetdrop-api/build.gradle index 2f4b4a8b..fd8de7cb 100644 --- a/backend/streetdrop-api/build.gradle +++ b/backend/streetdrop-api/build.gradle @@ -13,6 +13,10 @@ jar { enabled = false } +ext { + set('springCloudVersion', "2022.0.3") +} + dependencies { implementation project(':streetdrop-domain') implementation project(':streetdrop-common') @@ -39,6 +43,13 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-webflux' implementation 'io.awspring.cloud:spring-cloud-starter-aws:2.4.4' + implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' +} + +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" + } } From 8ba6e7c809feb0109da7043413983bfdde1ce775 Mon Sep 17 00:00:00 2001 From: siyeonSon Date: Sun, 8 Sep 2024 14:27:48 +0900 Subject: [PATCH 02/16] :sparkles: feat(api): get 50 popular songs using Apple Music API --- .../config/AppleMusicAuthConfig.java | 22 ++++++ .../response/MusicInfoListResponseDto.java | 26 +++++++ .../dto/response/MusicInfoResponseDto.java | 45 +++++++++++ .../catalogchart/AppleMusicResponseDto.java | 74 +++++++++++++++++++ .../applemusic/service/AppleMusicService.java | 21 ++++++ .../feign/client/AppleMusicFeignClient.java | 19 +++++ .../external/feign/config/FeignConfig.java | 10 +++ 7 files changed, 217 insertions(+) create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/config/AppleMusicAuthConfig.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/MusicInfoListResponseDto.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/MusicInfoResponseDto.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/catalogchart/AppleMusicResponseDto.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/external/feign/client/AppleMusicFeignClient.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/external/feign/config/FeignConfig.java diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/config/AppleMusicAuthConfig.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/config/AppleMusicAuthConfig.java new file mode 100644 index 00000000..37e325f9 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/config/AppleMusicAuthConfig.java @@ -0,0 +1,22 @@ +package com.depromeet.external.applemusic.config; + +import feign.RequestInterceptor; +import feign.RequestTemplate; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class AppleMusicAuthConfig implements RequestInterceptor { + + private static final String HEADER_NAME ="Authorization"; + private static final String AUTH_TYPE = "Bearer"; + + @Value("${apple.music.api.key}") + private String value; + + @Override + public void apply(RequestTemplate requestTemplate) { + requestTemplate.header(HEADER_NAME, AUTH_TYPE + " " + value); + } + +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/MusicInfoListResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/MusicInfoListResponseDto.java new file mode 100644 index 00000000..2068ba6e --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/MusicInfoListResponseDto.java @@ -0,0 +1,26 @@ +package com.depromeet.external.applemusic.dto.response; + +import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicResponseDto; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class MusicInfoListResponseDto { + + public List data; + + public static MusicInfoListResponseDto ofAppleMusicResponseDto ( + AppleMusicResponseDto appleMusicResponseDto + ) { + MusicInfoListResponseDto musicResponseDto = new MusicInfoListResponseDto(); + musicResponseDto.data = Optional.ofNullable(appleMusicResponseDto.results.songs.get(0)) + .map(songs -> songs.data.stream() + .map(MusicInfoResponseDto::fromAppleMusicResponse) + .toList() + ) + .orElse(Collections.emptyList()); + return musicResponseDto; + } + +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/MusicInfoResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/MusicInfoResponseDto.java new file mode 100644 index 00000000..b1e14dfd --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/MusicInfoResponseDto.java @@ -0,0 +1,45 @@ +package com.depromeet.external.applemusic.dto.response; + +import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicResponseDto; + +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; + +public class MusicInfoResponseDto { + public String albumName; + public String artistName; + public String songName; + public String durationTime; + public String albumImage; + public String albumThumbnailImage; + public List genre; + + public static MusicInfoResponseDto fromAppleMusicResponse(AppleMusicResponseDto.SongData data) { + final int MINUTES_PER_HOUR = 60; + final int TO_SEC = 1000; + final int ALBUM_IMAGE_SIZE = 500; + final int ALBUM_THUMBNAIL_IMAGE_SIZE = 100; + + BiFunction fillSize = (s, size) -> + s.replace("{w}", String.valueOf(size)).replace("{h}", String.valueOf(size)); + + Function totalSecondsToTime = totalSeconds -> { + totalSeconds = totalSeconds / TO_SEC; + int minutes = totalSeconds / MINUTES_PER_HOUR; + int seconds = totalSeconds % MINUTES_PER_HOUR; + return String.format("%d:%02d", minutes, seconds); + }; + + MusicInfoResponseDto musicInfo = new MusicInfoResponseDto(); + musicInfo.albumName = data.attributes.albumName; + musicInfo.artistName = data.attributes.artistName; + musicInfo.songName = data.attributes.name; + musicInfo.durationTime = totalSecondsToTime.apply(data.attributes.durationInMillis); + musicInfo.albumImage = fillSize.apply(data.attributes.artwork.url, ALBUM_IMAGE_SIZE); + musicInfo.albumThumbnailImage = fillSize.apply(data.attributes.artwork.url, ALBUM_THUMBNAIL_IMAGE_SIZE); + musicInfo.genre = data.attributes.genreNames; + return musicInfo; + } + +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/catalogchart/AppleMusicResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/catalogchart/AppleMusicResponseDto.java new file mode 100644 index 00000000..b9586534 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/catalogchart/AppleMusicResponseDto.java @@ -0,0 +1,74 @@ +package com.depromeet.external.applemusic.dto.response.catalogchart; + +import java.util.List; + +public class AppleMusicResponseDto { + public Results results; + public Meta meta; + + public static class Results { + public List songs; + } + + public static class Song { + public String chart; + public String name; + public String orderId; + public String next; + public List data; + public String href; + } + + public static class SongData { + public String id; + public String type; + public String href; + public SongAttributes attributes; + } + + public static class SongAttributes { + public String albumName; + public List genreNames; + public int trackNumber; + public int durationInMillis; + public String releaseDate; + public String isrc; + public Artwork artwork; + public String url; + public PlayParams playParams; + public int discNumber; + public boolean hasLyrics; + public boolean isAppleDigitalMaster; + public String name; + public List previews; + public String artistName; + } + + public static class Artwork { + public int width; + public int height; + public String url; + public String bgColor; + public String textColor1; + public String textColor2; + public String textColor3; + public String textColor4; + } + + public static class PlayParams { + public String id; + public String kind; + } + + public static class Preview { + public String url; + } + + public static class Meta { + public MetaResults results; + } + + public static class MetaResults { + public List order; + } +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java new file mode 100644 index 00000000..090b717e --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java @@ -0,0 +1,21 @@ +package com.depromeet.external.applemusic.service; + +import com.depromeet.external.applemusic.dto.response.MusicInfoListResponseDto; +import com.depromeet.external.feign.client.AppleMusicFeignClient; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class AppleMusicService { + + private final AppleMusicFeignClient appleMusicFeignClient; + + public MusicInfoListResponseDto getSongCharts() { + var response = appleMusicFeignClient.getCatalogCharts("songs", 50); + return MusicInfoListResponseDto.ofAppleMusicResponseDto(response); + } + +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/feign/client/AppleMusicFeignClient.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/feign/client/AppleMusicFeignClient.java new file mode 100644 index 00000000..dba14ad3 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/feign/client/AppleMusicFeignClient.java @@ -0,0 +1,19 @@ +package com.depromeet.external.feign.client; + +import com.depromeet.external.applemusic.config.AppleMusicAuthConfig; +import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicResponseDto; +import com.depromeet.external.feign.config.FeignConfig; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient(name = "openApiFeignClient", url = "${apple.music.api.base-url}", + configuration = {FeignConfig.class, AppleMusicAuthConfig.class}) +public interface AppleMusicFeignClient { + @GetMapping(value = "${apple.music.api.get-catalog-charts-url}", produces = MediaType.APPLICATION_JSON_VALUE) + AppleMusicResponseDto getCatalogCharts( + @RequestParam("types") String types, + @RequestParam("limit") int limit + ); +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/feign/config/FeignConfig.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/feign/config/FeignConfig.java new file mode 100644 index 00000000..a2ac06ae --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/feign/config/FeignConfig.java @@ -0,0 +1,10 @@ +package com.depromeet.external.feign.config; + +import com.depromeet.external.feign.client.AppleMusicFeignClient; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableFeignClients(clients = AppleMusicFeignClient.class) +public class FeignConfig { +} From 77ceeb950bf8d541b3540807f589c8589462b12f Mon Sep 17 00:00:00 2001 From: siyeonSon Date: Sun, 8 Sep 2024 14:34:47 +0900 Subject: [PATCH 03/16] :sparkles: chore(api): change songs limit 50 -> 30 --- .../external/applemusic/service/AppleMusicService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java index 090b717e..d4668291 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java @@ -14,7 +14,7 @@ public class AppleMusicService { private final AppleMusicFeignClient appleMusicFeignClient; public MusicInfoListResponseDto getSongCharts() { - var response = appleMusicFeignClient.getCatalogCharts("songs", 50); + var response = appleMusicFeignClient.getCatalogCharts("songs", 30); return MusicInfoListResponseDto.ofAppleMusicResponseDto(response); } From 36211260b002b4f67ff667e6e52dde776e95e738 Mon Sep 17 00:00:00 2001 From: siyeonSon Date: Wed, 11 Sep 2024 10:25:40 +0900 Subject: [PATCH 04/16] :sparkles: feat(api): get recent posted songs --- .../domains/music/service/MusicService.java | 7 +++ .../repository/QueryDslSongRepository.java | 9 ++++ .../music/song/repository/SongRepository.java | 2 +- .../song/repository/SongRepositoryImpl.java | 49 +++++++++++++++++++ .../service/SearchRecommendService.java | 16 ++++++ 5 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/QueryDslSongRepository.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepositoryImpl.java diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java index 758a50f8..0b53b147 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java @@ -8,6 +8,7 @@ import com.depromeet.domains.music.dto.response.MusicResponseDto; import com.depromeet.domains.music.event.CreateSongGenreEvent; import com.depromeet.domains.music.song.repository.SongRepository; +import com.depromeet.domains.recommend.dto.response.MusicInfoListResponseDto; import com.depromeet.music.album.Album; import com.depromeet.music.album.AlbumCover; import com.depromeet.music.artist.Artist; @@ -120,4 +121,10 @@ public MusicResponseDto getMusic(Long songId) { .map(MusicResponseDto::new) .orElseThrow(() -> new NotFoundException(CommonErrorCode.NOT_FOUND, songId)); } + + @Transactional(readOnly = true) + public MusicInfoListResponseDto getRecentMusic(int count) { + var recentSongs = songRepository.findRecentSongs(count); + return new MusicInfoListResponseDto(recentSongs); + } } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/QueryDslSongRepository.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/QueryDslSongRepository.java new file mode 100644 index 00000000..e56a60d5 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/QueryDslSongRepository.java @@ -0,0 +1,9 @@ +package com.depromeet.domains.music.song.repository; + +import com.depromeet.domains.recommend.dto.response.MusicInfoResponseDto; + +import java.util.List; + +public interface QueryDslSongRepository { + List findRecentSongs(int count); +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepository.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepository.java index 9adc35ba..802c77df 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepository.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepository.java @@ -7,7 +7,7 @@ import java.util.Optional; -public interface SongRepository extends JpaRepository { +public interface SongRepository extends JpaRepository, QueryDslSongRepository { Optional findSongByNameAndAlbum(String name, Album album); @Query("SELECT s FROM Song s JOIN FETCH s.album " + diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepositoryImpl.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepositoryImpl.java new file mode 100644 index 00000000..ff395fea --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepositoryImpl.java @@ -0,0 +1,49 @@ +package com.depromeet.domains.music.song.repository; + +import com.depromeet.domains.recommend.dto.response.MusicInfoResponseDto; +import com.querydsl.core.types.Projections; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +import static com.depromeet.item.QItem.item; +import static com.depromeet.music.album.QAlbum.album; +import static com.depromeet.music.album.QAlbumCover.albumCover; +import static com.depromeet.music.artist.QArtist.artist; +import static com.depromeet.music.song.QSong.song; + +@Repository +@RequiredArgsConstructor +public class SongRepositoryImpl implements QueryDslSongRepository { + + private final JPAQueryFactory queryFactory; + + @Override + public List findRecentSongs(int count) { + return queryFactory.select( + Projections.fields( + MusicInfoResponseDto.class, + album.name, + artist.name, + song.name, + albumCover.albumImage, + albumCover.albumThumbnail, + song.genres + )) + .from(item) + .join(item.song, song) + .on(item.song.id.eq(song.id)) + .join(song.album, album) + .on(song.album.id.eq(album.id)) + .join(album.artist, artist) + .on(album.artist.id.eq(artist.id)) + .join(album.albumCover, albumCover) + .on(album.albumCover.id.eq(albumCover.id)) + .orderBy(item.createdAt.desc()) + .limit(count) + .fetch(); + } + +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java index c7654149..e12657c4 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java @@ -2,16 +2,23 @@ import com.depromeet.common.error.dto.CommonErrorCode; import com.depromeet.common.error.exception.internal.BusinessException; +import com.depromeet.domains.music.service.MusicService; import com.depromeet.domains.recommend.dto.response.SearchTermRecommendResponseDto; import com.depromeet.domains.recommend.dto.response.TextColorDto; import com.depromeet.domains.recommend.repository.SearchRecommendTermRepository; +import com.depromeet.domains.recommend.dto.response.MusicInfoListResponseDto; +import com.depromeet.external.applemusic.service.AppleMusicService; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +@Slf4j @Service @RequiredArgsConstructor public class SearchRecommendService { + private final MusicService musicService; + private final AppleMusicService appleMusicService; private final SearchRecommendTermRepository searchRecommendTermRepository; public SearchTermRecommendResponseDto recommendSearchTerm() { @@ -30,4 +37,13 @@ public SearchTermRecommendResponseDto recommendSearchTerm() { return new SearchTermRecommendResponseDto(description, termList); } + + public void recommendSearchSongs() { + // 음악 추천 30개 + MusicInfoListResponseDto chartMusicRecommend = appleMusicService.getSongCharts(30); + + // 최근에 드롭된 음악 15개 + MusicInfoListResponseDto recentMusicRecommend = musicService.getRecentMusic(15); + } + } From 818872f2538c8487f6a9859c585efb1c53e8b950 Mon Sep 17 00:00:00 2001 From: siyeonSon Date: Wed, 11 Sep 2024 13:54:44 +0900 Subject: [PATCH 05/16] :sparkles: feat(api): recommend artists --- .../response/ArtistInfoListResponseDto.java | 27 +++++++ .../dto/response/ArtistInfoResponseDto.java | 39 ++++++++++ .../response/MusicInfoListResponseDto.java | 27 +++++++ .../dto/response/MusicInfoResponseDto.java | 35 ++++++--- .../service/SearchRecommendService.java | 7 +- .../response/MusicInfoListResponseDto.java | 26 ------- .../AppleMusicAlbumChartResponseDto.java | 75 +++++++++++++++++++ ...va => AppleMusicSongChartResponseDto.java} | 2 +- .../applemusic/service/AppleMusicService.java | 14 ++-- .../feign/client/AppleMusicFeignClient.java | 11 ++- 10 files changed, 215 insertions(+), 48 deletions(-) create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/ArtistInfoListResponseDto.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/ArtistInfoResponseDto.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoListResponseDto.java rename backend/streetdrop-api/src/main/java/com/depromeet/{external/applemusic => domains/recommend}/dto/response/MusicInfoResponseDto.java (60%) delete mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/MusicInfoListResponseDto.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/catalogchart/AppleMusicAlbumChartResponseDto.java rename backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/catalogchart/{AppleMusicResponseDto.java => AppleMusicSongChartResponseDto.java} (97%) diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/ArtistInfoListResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/ArtistInfoListResponseDto.java new file mode 100644 index 00000000..2ae6d004 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/ArtistInfoListResponseDto.java @@ -0,0 +1,27 @@ +package com.depromeet.domains.recommend.dto.response; + +import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicAlbumChartResponseDto; +import lombok.AllArgsConstructor; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +@AllArgsConstructor +public class ArtistInfoListResponseDto { + + private List data; + + public static ArtistInfoListResponseDto ofAppleMusicResponseDto(AppleMusicAlbumChartResponseDto appleMusicAlbumChartResponseDto) { + List artistInfoList = + Optional.ofNullable(appleMusicAlbumChartResponseDto.results.albums) + .filter(albums -> !albums.isEmpty()) + .map(albums -> albums.get(0).data.stream() + .map(ArtistInfoResponseDto::fromAppleMusicResponse) + .toList() + ) + .orElse(Collections.emptyList()); + return new ArtistInfoListResponseDto(artistInfoList); + } + +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/ArtistInfoResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/ArtistInfoResponseDto.java new file mode 100644 index 00000000..51f7d3ab --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/ArtistInfoResponseDto.java @@ -0,0 +1,39 @@ +package com.depromeet.domains.recommend.dto.response; + +import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicAlbumChartResponseDto; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@AllArgsConstructor +public class ArtistInfoResponseDto { + + private static final int MINUTES_PER_HOUR = 60; + private static final int TO_SEC = 1000; + private static final int ALBUM_IMAGE_SIZE = 500; + private static final int ALBUM_THUMBNAIL_IMAGE_SIZE = 100; + + private String artistName; + private String albumImage; + private String albumThumbnailImage; + + private static String fillSize(String url, int size) { + return url.replace("{w}", String.valueOf(size)).replace("{h}", String.valueOf(size)); + } + + private static String convertToTimeFormat(int totalMilliseconds) { + int totalSeconds = totalMilliseconds / TO_SEC; + int minutes = totalSeconds / MINUTES_PER_HOUR; + int seconds = totalSeconds % MINUTES_PER_HOUR; + return String.format("%d:%02d", minutes, seconds); + } + + public static ArtistInfoResponseDto fromAppleMusicResponse(AppleMusicAlbumChartResponseDto.AlbumData data) { + return new ArtistInfoResponseDto( + data.attributes.artistName, + fillSize(data.attributes.artwork.url, ALBUM_IMAGE_SIZE), + fillSize(data.attributes.artwork.url, ALBUM_THUMBNAIL_IMAGE_SIZE) + ); + } + +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoListResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoListResponseDto.java new file mode 100644 index 00000000..87fd5ca1 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoListResponseDto.java @@ -0,0 +1,27 @@ +package com.depromeet.domains.recommend.dto.response; + +import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicSongChartResponseDto; +import lombok.AllArgsConstructor; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +@AllArgsConstructor +public class MusicInfoListResponseDto { + + private List content; + + public static MusicInfoListResponseDto ofAppleMusicResponseDto(AppleMusicSongChartResponseDto appleMusicSongChartResponseDto) { + List musicInfoList = Optional.ofNullable(appleMusicSongChartResponseDto.results.songs) + .filter(songs -> !songs.isEmpty()) + .map(songs -> songs.get(0).data.stream() + .map(MusicInfoResponseDto::fromAppleMusicResponse) + .toList() + ) + .orElse(Collections.emptyList()); + + return new MusicInfoListResponseDto(musicInfoList); + } + +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/MusicInfoResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoResponseDto.java similarity index 60% rename from backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/MusicInfoResponseDto.java rename to backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoResponseDto.java index b1e14dfd..f6e4644d 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/MusicInfoResponseDto.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoResponseDto.java @@ -1,21 +1,34 @@ -package com.depromeet.external.applemusic.dto.response; +package com.depromeet.domains.recommend.dto.response; -import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicResponseDto; +import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicSongChartResponseDto; +import com.depromeet.music.genre.Genre; +import lombok.NoArgsConstructor; import java.util.List; import java.util.function.BiFunction; import java.util.function.Function; +@NoArgsConstructor public class MusicInfoResponseDto { - public String albumName; - public String artistName; - public String songName; - public String durationTime; - public String albumImage; - public String albumThumbnailImage; - public List genre; - - public static MusicInfoResponseDto fromAppleMusicResponse(AppleMusicResponseDto.SongData data) { + private String albumName; + private String artistName; + private String songName; + private String durationTime; + private String albumImage; + private String albumThumbnailImage; + private List genre; + + public MusicInfoResponseDto(String albumName, String artistName, String songName, String albumImage, String albumThumbnailImage, List genre) { + this.albumName = albumName; + this.artistName = artistName; + this.songName = songName; + this.durationTime = ""; // TODO: DB에서 가져오는 음악 데이터는 durationTime이 없음 + this.albumImage = albumImage; + this.albumThumbnailImage = albumThumbnailImage; + this.genre = genre.stream().map(Genre::getName).toList(); + } + + public static MusicInfoResponseDto fromAppleMusicResponse(AppleMusicSongChartResponseDto.SongData data) { final int MINUTES_PER_HOUR = 60; final int TO_SEC = 1000; final int ALBUM_IMAGE_SIZE = 500; diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java index e12657c4..b479b13f 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java @@ -3,10 +3,8 @@ import com.depromeet.common.error.dto.CommonErrorCode; import com.depromeet.common.error.exception.internal.BusinessException; import com.depromeet.domains.music.service.MusicService; -import com.depromeet.domains.recommend.dto.response.SearchTermRecommendResponseDto; -import com.depromeet.domains.recommend.dto.response.TextColorDto; +import com.depromeet.domains.recommend.dto.response.*; import com.depromeet.domains.recommend.repository.SearchRecommendTermRepository; -import com.depromeet.domains.recommend.dto.response.MusicInfoListResponseDto; import com.depromeet.external.applemusic.service.AppleMusicService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -44,6 +42,9 @@ public void recommendSearchSongs() { // 최근에 드롭된 음악 15개 MusicInfoListResponseDto recentMusicRecommend = musicService.getRecentMusic(15); + + // 아티스트 추천 10개 + ArtistInfoListResponseDto chartArtistRecommend = appleMusicService.getArtistCharts(10); } } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/MusicInfoListResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/MusicInfoListResponseDto.java deleted file mode 100644 index 2068ba6e..00000000 --- a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/MusicInfoListResponseDto.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.depromeet.external.applemusic.dto.response; - -import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicResponseDto; - -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -public class MusicInfoListResponseDto { - - public List data; - - public static MusicInfoListResponseDto ofAppleMusicResponseDto ( - AppleMusicResponseDto appleMusicResponseDto - ) { - MusicInfoListResponseDto musicResponseDto = new MusicInfoListResponseDto(); - musicResponseDto.data = Optional.ofNullable(appleMusicResponseDto.results.songs.get(0)) - .map(songs -> songs.data.stream() - .map(MusicInfoResponseDto::fromAppleMusicResponse) - .toList() - ) - .orElse(Collections.emptyList()); - return musicResponseDto; - } - -} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/catalogchart/AppleMusicAlbumChartResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/catalogchart/AppleMusicAlbumChartResponseDto.java new file mode 100644 index 00000000..96b68202 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/catalogchart/AppleMusicAlbumChartResponseDto.java @@ -0,0 +1,75 @@ +package com.depromeet.external.applemusic.dto.response.catalogchart; + +import java.util.List; + +public class AppleMusicAlbumChartResponseDto { + public Results results; + public Meta meta; + + public static class Results { + public List albums; + } + + public static class Album { + public String chart; + public String name; + public String orderId; + public String next; + public List data; + public String href; + } + + public static class AlbumData { + public String id; + public String type; + public String href; + public AlbumAttributes attributes; + } + + public static class AlbumAttributes { + public String copyright; + public List genreNames; + public String releaseDate; + public boolean isMasteredForItunes; + public String upc; + public Artwork artwork; + public PlayParams playParams; + public String url; + public String recordLabel; + public boolean isCompilation; + public int trackCount; + public boolean isSingle; + public String name; + public String artistName; + public EditorialNotes editorialNotes; + public boolean isComplete; + } + + public static class Artwork { + public int width; + public int height; + public String url; + public String bgColor; + public String textColor1; + public String textColor2; + public String textColor3; + public String textColor4; + } + + public static class PlayParams { + public String id; + public String kind; + } + + public static class EditorialNotes { + public String shortNote; + } + + public static class Meta { + public MetaResults results; + } + + public static class MetaResults { + public List order; + } +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/catalogchart/AppleMusicResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/catalogchart/AppleMusicSongChartResponseDto.java similarity index 97% rename from backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/catalogchart/AppleMusicResponseDto.java rename to backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/catalogchart/AppleMusicSongChartResponseDto.java index b9586534..6788c576 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/catalogchart/AppleMusicResponseDto.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/dto/response/catalogchart/AppleMusicSongChartResponseDto.java @@ -2,7 +2,7 @@ import java.util.List; -public class AppleMusicResponseDto { +public class AppleMusicSongChartResponseDto { public Results results; public Meta meta; diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java index d4668291..9fb36e69 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java @@ -1,21 +1,25 @@ package com.depromeet.external.applemusic.service; -import com.depromeet.external.applemusic.dto.response.MusicInfoListResponseDto; +import com.depromeet.domains.recommend.dto.response.ArtistInfoListResponseDto; +import com.depromeet.domains.recommend.dto.response.MusicInfoListResponseDto; import com.depromeet.external.feign.client.AppleMusicFeignClient; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -@Slf4j @Service @RequiredArgsConstructor public class AppleMusicService { private final AppleMusicFeignClient appleMusicFeignClient; - public MusicInfoListResponseDto getSongCharts() { - var response = appleMusicFeignClient.getCatalogCharts("songs", 30); + public MusicInfoListResponseDto getSongCharts(int limit) { + var response = appleMusicFeignClient.getSongCharts("songs", limit); return MusicInfoListResponseDto.ofAppleMusicResponseDto(response); } + public ArtistInfoListResponseDto getArtistCharts(int limit) { + var response = appleMusicFeignClient.getAlbumCharts("albums", limit); + return ArtistInfoListResponseDto.ofAppleMusicResponseDto(response); + } + } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/feign/client/AppleMusicFeignClient.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/feign/client/AppleMusicFeignClient.java index dba14ad3..30ed73fa 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/external/feign/client/AppleMusicFeignClient.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/feign/client/AppleMusicFeignClient.java @@ -1,7 +1,8 @@ package com.depromeet.external.feign.client; import com.depromeet.external.applemusic.config.AppleMusicAuthConfig; -import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicResponseDto; +import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicAlbumChartResponseDto; +import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicSongChartResponseDto; import com.depromeet.external.feign.config.FeignConfig; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.http.MediaType; @@ -12,7 +13,13 @@ configuration = {FeignConfig.class, AppleMusicAuthConfig.class}) public interface AppleMusicFeignClient { @GetMapping(value = "${apple.music.api.get-catalog-charts-url}", produces = MediaType.APPLICATION_JSON_VALUE) - AppleMusicResponseDto getCatalogCharts( + AppleMusicSongChartResponseDto getSongCharts( + @RequestParam("types") String types, + @RequestParam("limit") int limit + ); + + @GetMapping(value = "${apple.music.api.get-catalog-charts-url}", produces = MediaType.APPLICATION_JSON_VALUE) + AppleMusicAlbumChartResponseDto getAlbumCharts( @RequestParam("types") String types, @RequestParam("limit") int limit ); From 397c29377d3d42542a216001aee817dca605107e Mon Sep 17 00:00:00 2001 From: siyeonSon Date: Wed, 11 Sep 2024 14:06:14 +0900 Subject: [PATCH 06/16] :sparkles: feat(api): recommend artists --- .../domains/recommend/dto/response/MusicInfoListResponseDto.java | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoListResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoListResponseDto.java index 87fd5ca1..01270c7d 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoListResponseDto.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoListResponseDto.java @@ -20,7 +20,6 @@ public static MusicInfoListResponseDto ofAppleMusicResponseDto(AppleMusicSongCha .toList() ) .orElse(Collections.emptyList()); - return new MusicInfoListResponseDto(musicInfoList); } From 0aaa7c42996bf5c3f7266c87d448f4ac5b7d82be Mon Sep 17 00:00:00 2001 From: siyeonSon Date: Wed, 11 Sep 2024 14:30:57 +0900 Subject: [PATCH 07/16] :sparkles: feat(api): recommend recent posted songs and popular songs and popular artists --- .../domains/music/service/MusicService.java | 6 +-- .../controller/SearchRecommendController.java | 13 +++++- .../response/ArtistInfoListResponseDto.java | 27 ----------- .../dto/response/ArtistInfoResponseDto.java | 2 + .../response/MusicInfoListResponseDto.java | 26 ----------- .../dto/response/MusicInfoResponseDto.java | 2 + .../dto/response/RecommendCategoryDto.java | 46 +++++++++++++++++++ .../dto/response/RecommendResponseDto.java | 12 +++++ .../service/SearchRecommendService.java | 14 ++++-- .../applemusic/service/AppleMusicService.java | 11 ++--- 10 files changed, 91 insertions(+), 68 deletions(-) delete mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/ArtistInfoListResponseDto.java delete mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoListResponseDto.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/RecommendCategoryDto.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/RecommendResponseDto.java diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java index 0b53b147..d789e777 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java @@ -8,7 +8,7 @@ import com.depromeet.domains.music.dto.response.MusicResponseDto; import com.depromeet.domains.music.event.CreateSongGenreEvent; import com.depromeet.domains.music.song.repository.SongRepository; -import com.depromeet.domains.recommend.dto.response.MusicInfoListResponseDto; +import com.depromeet.domains.recommend.dto.response.RecommendCategoryDto; import com.depromeet.music.album.Album; import com.depromeet.music.album.AlbumCover; import com.depromeet.music.artist.Artist; @@ -123,8 +123,8 @@ public MusicResponseDto getMusic(Long songId) { } @Transactional(readOnly = true) - public MusicInfoListResponseDto getRecentMusic(int count) { + public RecommendCategoryDto getRecentMusic(int count) { var recentSongs = songRepository.findRecentSongs(count); - return new MusicInfoListResponseDto(recentSongs); + return RecommendCategoryDto.ofMusicInfoResponseDto(recentSongs); } } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/controller/SearchRecommendController.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/controller/SearchRecommendController.java index a6a3bb4c..4aab2d0c 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/controller/SearchRecommendController.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/controller/SearchRecommendController.java @@ -1,6 +1,7 @@ package com.depromeet.domains.recommend.controller; import com.depromeet.common.dto.ResponseDto; +import com.depromeet.domains.recommend.dto.response.RecommendResponseDto; import com.depromeet.domains.recommend.dto.response.SearchTermRecommendResponseDto; import com.depromeet.domains.recommend.service.SearchRecommendService; import io.swagger.v3.oas.annotations.Operation; @@ -12,17 +13,25 @@ import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping("/search-term/recommend") +@RequestMapping @RequiredArgsConstructor @Tag(name = "💁Search Recommend", description = "Search Recommend API") public class SearchRecommendController { + private final SearchRecommendService searchRecommendService; @Operation(summary = "검색어 추천") - @GetMapping + @GetMapping("/search-term/recommend") public ResponseEntity recommendSearchTerm() { var response = searchRecommendService.recommendSearchTerm(); return ResponseDto.ok(response); } + @Operation(summary = "검색어 추천 v2") + @GetMapping("/v2/search-term/recommend") + public ResponseEntity recommendSearchTerm2() { + var response = searchRecommendService.recommendSearchSongs(); + return ResponseDto.ok(response); + } + } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/ArtistInfoListResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/ArtistInfoListResponseDto.java deleted file mode 100644 index 2ae6d004..00000000 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/ArtistInfoListResponseDto.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.depromeet.domains.recommend.dto.response; - -import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicAlbumChartResponseDto; -import lombok.AllArgsConstructor; - -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -@AllArgsConstructor -public class ArtistInfoListResponseDto { - - private List data; - - public static ArtistInfoListResponseDto ofAppleMusicResponseDto(AppleMusicAlbumChartResponseDto appleMusicAlbumChartResponseDto) { - List artistInfoList = - Optional.ofNullable(appleMusicAlbumChartResponseDto.results.albums) - .filter(albums -> !albums.isEmpty()) - .map(albums -> albums.get(0).data.stream() - .map(ArtistInfoResponseDto::fromAppleMusicResponse) - .toList() - ) - .orElse(Collections.emptyList()); - return new ArtistInfoListResponseDto(artistInfoList); - } - -} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/ArtistInfoResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/ArtistInfoResponseDto.java index 51f7d3ab..77535f6b 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/ArtistInfoResponseDto.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/ArtistInfoResponseDto.java @@ -2,8 +2,10 @@ import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicAlbumChartResponseDto; import lombok.AllArgsConstructor; +import lombok.Getter; import lombok.NoArgsConstructor; +@Getter @NoArgsConstructor @AllArgsConstructor public class ArtistInfoResponseDto { diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoListResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoListResponseDto.java deleted file mode 100644 index 01270c7d..00000000 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoListResponseDto.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.depromeet.domains.recommend.dto.response; - -import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicSongChartResponseDto; -import lombok.AllArgsConstructor; - -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -@AllArgsConstructor -public class MusicInfoListResponseDto { - - private List content; - - public static MusicInfoListResponseDto ofAppleMusicResponseDto(AppleMusicSongChartResponseDto appleMusicSongChartResponseDto) { - List musicInfoList = Optional.ofNullable(appleMusicSongChartResponseDto.results.songs) - .filter(songs -> !songs.isEmpty()) - .map(songs -> songs.get(0).data.stream() - .map(MusicInfoResponseDto::fromAppleMusicResponse) - .toList() - ) - .orElse(Collections.emptyList()); - return new MusicInfoListResponseDto(musicInfoList); - } - -} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoResponseDto.java index f6e4644d..5938f747 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoResponseDto.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoResponseDto.java @@ -2,12 +2,14 @@ import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicSongChartResponseDto; import com.depromeet.music.genre.Genre; +import lombok.Getter; import lombok.NoArgsConstructor; import java.util.List; import java.util.function.BiFunction; import java.util.function.Function; +@Getter @NoArgsConstructor public class MusicInfoResponseDto { private String albumName; diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/RecommendCategoryDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/RecommendCategoryDto.java new file mode 100644 index 00000000..de066014 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/RecommendCategoryDto.java @@ -0,0 +1,46 @@ +package com.depromeet.domains.recommend.dto.response; + +import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicAlbumChartResponseDto; +import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicSongChartResponseDto; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +@Getter +@AllArgsConstructor +public class RecommendCategoryDto { + private String title; + private List content; + private boolean nextPage; + + public static RecommendCategoryDto ofMusicInfoResponseDto(List musicInfoResponseDto) { + return new RecommendCategoryDto("많이 드랍된 음악", musicInfoResponseDto, true); + } + + + public static RecommendCategoryDto ofAppleMusicResponseDto(AppleMusicSongChartResponseDto appleMusicSongChartResponseDto) { + List musicInfoList = Optional.ofNullable(appleMusicSongChartResponseDto.results.songs) + .filter(songs -> !songs.isEmpty()) + .map(songs -> songs.get(0).data.stream() + .map(MusicInfoResponseDto::fromAppleMusicResponse) + .toList() + ) + .orElse(Collections.emptyList()); + return new RecommendCategoryDto("인기 있는 음악", musicInfoList, true); + } + + public static RecommendCategoryDto ofAppleMusicResponseDto(AppleMusicAlbumChartResponseDto appleMusicAlbumChartResponseDto) { + List artistInfoList = + Optional.ofNullable(appleMusicAlbumChartResponseDto.results.albums) + .filter(albums -> !albums.isEmpty()) + .map(albums -> albums.get(0).data.stream() + .map(ArtistInfoResponseDto::fromAppleMusicResponse) + .toList() + ) + .orElse(Collections.emptyList()); + return new RecommendCategoryDto("아티스트", artistInfoList, false); + } +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/RecommendResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/RecommendResponseDto.java new file mode 100644 index 00000000..a0f689b3 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/RecommendResponseDto.java @@ -0,0 +1,12 @@ +package com.depromeet.domains.recommend.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.List; + +@Getter +@AllArgsConstructor +public class RecommendResponseDto { + private List data; +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java index b479b13f..6b4e8d11 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java @@ -10,6 +10,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.util.List; + @Slf4j @Service @RequiredArgsConstructor @@ -36,15 +38,19 @@ public SearchTermRecommendResponseDto recommendSearchTerm() { return new SearchTermRecommendResponseDto(description, termList); } - public void recommendSearchSongs() { + public RecommendResponseDto recommendSearchSongs() { // 음악 추천 30개 - MusicInfoListResponseDto chartMusicRecommend = appleMusicService.getSongCharts(30); + RecommendCategoryDto chartMusicRecommend = appleMusicService.getSongCharts(30); // 최근에 드롭된 음악 15개 - MusicInfoListResponseDto recentMusicRecommend = musicService.getRecentMusic(15); + RecommendCategoryDto recentMusicRecommend = musicService.getRecentMusic(15); // 아티스트 추천 10개 - ArtistInfoListResponseDto chartArtistRecommend = appleMusicService.getArtistCharts(10); + RecommendCategoryDto chartArtistRecommend = appleMusicService.getArtistCharts(10); + + return new RecommendResponseDto( + List.of(chartMusicRecommend, recentMusicRecommend, chartArtistRecommend) + ); } } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java index 9fb36e69..58254b81 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java @@ -1,7 +1,6 @@ package com.depromeet.external.applemusic.service; -import com.depromeet.domains.recommend.dto.response.ArtistInfoListResponseDto; -import com.depromeet.domains.recommend.dto.response.MusicInfoListResponseDto; +import com.depromeet.domains.recommend.dto.response.RecommendCategoryDto; import com.depromeet.external.feign.client.AppleMusicFeignClient; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -12,14 +11,14 @@ public class AppleMusicService { private final AppleMusicFeignClient appleMusicFeignClient; - public MusicInfoListResponseDto getSongCharts(int limit) { + public RecommendCategoryDto getSongCharts(int limit) { var response = appleMusicFeignClient.getSongCharts("songs", limit); - return MusicInfoListResponseDto.ofAppleMusicResponseDto(response); + return RecommendCategoryDto.ofAppleMusicResponseDto(response); } - public ArtistInfoListResponseDto getArtistCharts(int limit) { + public RecommendCategoryDto getArtistCharts(int limit) { var response = appleMusicFeignClient.getAlbumCharts("albums", limit); - return ArtistInfoListResponseDto.ofAppleMusicResponseDto(response); + return RecommendCategoryDto.ofAppleMusicResponseDto(response); } } From 23e0767cccd0d73ba3f967c83c51f0eb2c236b66 Mon Sep 17 00:00:00 2001 From: siyeonSon Date: Wed, 11 Sep 2024 17:56:15 +0900 Subject: [PATCH 08/16] :sparkles: refactor(api): manage constants in RecommendType enum class --- .../domains/music/service/MusicService.java | 7 ++++--- .../recommend/constant/RecommendType.java | 16 ++++++++++++++++ .../dto/response/RecommendCategoryDto.java | 13 +++++++------ .../service/SearchRecommendService.java | 16 ++++++---------- .../applemusic/service/AppleMusicService.java | 13 +++++++------ 5 files changed, 40 insertions(+), 25 deletions(-) create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/constant/RecommendType.java diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java index d789e777..a1b52899 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java @@ -9,6 +9,7 @@ import com.depromeet.domains.music.event.CreateSongGenreEvent; import com.depromeet.domains.music.song.repository.SongRepository; import com.depromeet.domains.recommend.dto.response.RecommendCategoryDto; +import com.depromeet.domains.recommend.constant.RecommendType; import com.depromeet.music.album.Album; import com.depromeet.music.album.AlbumCover; import com.depromeet.music.artist.Artist; @@ -123,8 +124,8 @@ public MusicResponseDto getMusic(Long songId) { } @Transactional(readOnly = true) - public RecommendCategoryDto getRecentMusic(int count) { - var recentSongs = songRepository.findRecentSongs(count); - return RecommendCategoryDto.ofMusicInfoResponseDto(recentSongs); + public RecommendCategoryDto getRecentMusic(RecommendType recommendType) { + var recentSongs = songRepository.findRecentSongs(recommendType.getLimit()); + return RecommendCategoryDto.ofMusicInfoResponseDto(recommendType, recentSongs); } } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/constant/RecommendType.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/constant/RecommendType.java new file mode 100644 index 00000000..7045040a --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/constant/RecommendType.java @@ -0,0 +1,16 @@ +package com.depromeet.domains.recommend.constant; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum RecommendType { + CHART_SONGS("인기 있는 음악", 30, true), + RECENT_SONGS("많이 드랍된 음악", 15, true), + CHART_ARTIST("아티스트", 10, false); + + private final String title; + private final int limit; + private final boolean nextPage; +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/RecommendCategoryDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/RecommendCategoryDto.java index de066014..026bd1dd 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/RecommendCategoryDto.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/RecommendCategoryDto.java @@ -1,5 +1,6 @@ package com.depromeet.domains.recommend.dto.response; +import com.depromeet.domains.recommend.constant.RecommendType; import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicAlbumChartResponseDto; import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicSongChartResponseDto; import lombok.AllArgsConstructor; @@ -16,12 +17,12 @@ public class RecommendCategoryDto { private List content; private boolean nextPage; - public static RecommendCategoryDto ofMusicInfoResponseDto(List musicInfoResponseDto) { - return new RecommendCategoryDto("많이 드랍된 음악", musicInfoResponseDto, true); + public static RecommendCategoryDto ofMusicInfoResponseDto(RecommendType recommendType, List musicInfoResponseDto) { + return new RecommendCategoryDto(recommendType.getTitle(), musicInfoResponseDto, recommendType.isNextPage()); } - public static RecommendCategoryDto ofAppleMusicResponseDto(AppleMusicSongChartResponseDto appleMusicSongChartResponseDto) { + public static RecommendCategoryDto ofAppleMusicResponseDto(RecommendType recommendType, AppleMusicSongChartResponseDto appleMusicSongChartResponseDto) { List musicInfoList = Optional.ofNullable(appleMusicSongChartResponseDto.results.songs) .filter(songs -> !songs.isEmpty()) .map(songs -> songs.get(0).data.stream() @@ -29,10 +30,10 @@ public static RecommendCategoryDto ofAppleMusicResponseDto(AppleMusicSongChartRe .toList() ) .orElse(Collections.emptyList()); - return new RecommendCategoryDto("인기 있는 음악", musicInfoList, true); + return new RecommendCategoryDto(recommendType.getTitle(), musicInfoList, recommendType.isNextPage()); } - public static RecommendCategoryDto ofAppleMusicResponseDto(AppleMusicAlbumChartResponseDto appleMusicAlbumChartResponseDto) { + public static RecommendCategoryDto ofAppleMusicResponseDto(RecommendType recommendType, AppleMusicAlbumChartResponseDto appleMusicAlbumChartResponseDto) { List artistInfoList = Optional.ofNullable(appleMusicAlbumChartResponseDto.results.albums) .filter(albums -> !albums.isEmpty()) @@ -41,6 +42,6 @@ public static RecommendCategoryDto ofAppleMusicResponseDto(AppleMusicAlbumChartR .toList() ) .orElse(Collections.emptyList()); - return new RecommendCategoryDto("아티스트", artistInfoList, false); + return new RecommendCategoryDto(recommendType.getTitle(), artistInfoList, recommendType.isNextPage()); } } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java index 6b4e8d11..37a9330a 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java @@ -3,6 +3,7 @@ import com.depromeet.common.error.dto.CommonErrorCode; import com.depromeet.common.error.exception.internal.BusinessException; import com.depromeet.domains.music.service.MusicService; +import com.depromeet.domains.recommend.constant.RecommendType; import com.depromeet.domains.recommend.dto.response.*; import com.depromeet.domains.recommend.repository.SearchRecommendTermRepository; import com.depromeet.external.applemusic.service.AppleMusicService; @@ -39,17 +40,12 @@ public SearchTermRecommendResponseDto recommendSearchTerm() { } public RecommendResponseDto recommendSearchSongs() { - // 음악 추천 30개 - RecommendCategoryDto chartMusicRecommend = appleMusicService.getSongCharts(30); - - // 최근에 드롭된 음악 15개 - RecommendCategoryDto recentMusicRecommend = musicService.getRecentMusic(15); - - // 아티스트 추천 10개 - RecommendCategoryDto chartArtistRecommend = appleMusicService.getArtistCharts(10); - return new RecommendResponseDto( - List.of(chartMusicRecommend, recentMusicRecommend, chartArtistRecommend) + List.of( + appleMusicService.getSongCharts(RecommendType.CHART_SONGS), + musicService.getRecentMusic(RecommendType.RECENT_SONGS), + appleMusicService.getArtistCharts(RecommendType.CHART_ARTIST) + ) ); } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java index 58254b81..e9ed8e88 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java @@ -1,6 +1,7 @@ package com.depromeet.external.applemusic.service; import com.depromeet.domains.recommend.dto.response.RecommendCategoryDto; +import com.depromeet.domains.recommend.constant.RecommendType; import com.depromeet.external.feign.client.AppleMusicFeignClient; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -11,14 +12,14 @@ public class AppleMusicService { private final AppleMusicFeignClient appleMusicFeignClient; - public RecommendCategoryDto getSongCharts(int limit) { - var response = appleMusicFeignClient.getSongCharts("songs", limit); - return RecommendCategoryDto.ofAppleMusicResponseDto(response); + public RecommendCategoryDto getSongCharts(RecommendType recommendType) { + var response = appleMusicFeignClient.getSongCharts("songs", recommendType.getLimit()); + return RecommendCategoryDto.ofAppleMusicResponseDto(recommendType, response); } - public RecommendCategoryDto getArtistCharts(int limit) { - var response = appleMusicFeignClient.getAlbumCharts("albums", limit); - return RecommendCategoryDto.ofAppleMusicResponseDto(response); + public RecommendCategoryDto getArtistCharts(RecommendType recommendType) { + var response = appleMusicFeignClient.getAlbumCharts("albums", recommendType.getLimit()); + return RecommendCategoryDto.ofAppleMusicResponseDto(recommendType, response); } } From 41da990cbff4231347e855adab6ccdc7b4a92390 Mon Sep 17 00:00:00 2001 From: siyeonSon Date: Wed, 18 Sep 2024 16:07:43 +0900 Subject: [PATCH 09/16] :sparkles: refactor(api): refactor the getCategoryChart() --- .../common/error/dto/CommonErrorCode.java | 4 ++-- .../service/SearchRecommendService.java | 4 ++-- .../applemusic/service/AppleMusicService.java | 22 ++++++++++++------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/CommonErrorCode.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/CommonErrorCode.java index c7b5c4ca..9bac9f43 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/CommonErrorCode.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/CommonErrorCode.java @@ -29,8 +29,8 @@ public enum CommonErrorCode implements ErrorCodeInterface { * Basic Server Error */ INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON_INTERNAL_SERVER_ERROR", "Internal Server Error", "An unexpected error occurred"), - NOT_IMPLEMENTED(HttpStatus.NOT_IMPLEMENTED, "COMMON_NOT_IMPLEMENTED", "Not Implemented", "The server does not support the functionality required to fulfill the request."); - + NOT_IMPLEMENTED(HttpStatus.NOT_IMPLEMENTED, "COMMON_NOT_IMPLEMENTED", "Not Implemented", "The server does not support the functionality required to fulfill the request."), + UNSUPPORTED_TYPE(HttpStatus.BAD_REQUEST, "COMMON_UNSUPPORTED_TYPE", "Unsupported Type", "The type specified is not supported."); private final HttpStatus status; private final String errorResponseCode; diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java index 37a9330a..d109a6b5 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java @@ -42,9 +42,9 @@ public SearchTermRecommendResponseDto recommendSearchTerm() { public RecommendResponseDto recommendSearchSongs() { return new RecommendResponseDto( List.of( - appleMusicService.getSongCharts(RecommendType.CHART_SONGS), + appleMusicService.getCategoryChart(RecommendType.CHART_SONGS), musicService.getRecentMusic(RecommendType.RECENT_SONGS), - appleMusicService.getArtistCharts(RecommendType.CHART_ARTIST) + appleMusicService.getCategoryChart(RecommendType.CHART_ARTIST) ) ); } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java index e9ed8e88..bf9a914d 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java @@ -1,5 +1,7 @@ package com.depromeet.external.applemusic.service; +import com.depromeet.common.error.dto.CommonErrorCode; +import com.depromeet.common.error.exception.internal.BusinessException; import com.depromeet.domains.recommend.dto.response.RecommendCategoryDto; import com.depromeet.domains.recommend.constant.RecommendType; import com.depromeet.external.feign.client.AppleMusicFeignClient; @@ -12,14 +14,18 @@ public class AppleMusicService { private final AppleMusicFeignClient appleMusicFeignClient; - public RecommendCategoryDto getSongCharts(RecommendType recommendType) { - var response = appleMusicFeignClient.getSongCharts("songs", recommendType.getLimit()); - return RecommendCategoryDto.ofAppleMusicResponseDto(recommendType, response); - } - - public RecommendCategoryDto getArtistCharts(RecommendType recommendType) { - var response = appleMusicFeignClient.getAlbumCharts("albums", recommendType.getLimit()); - return RecommendCategoryDto.ofAppleMusicResponseDto(recommendType, response); + public RecommendCategoryDto getCategoryChart(RecommendType recommendType) { + if (recommendType == RecommendType.CHART_SONGS) { + var response = appleMusicFeignClient.getSongCharts("songs", recommendType.getLimit()); + return RecommendCategoryDto.ofAppleMusicResponseDto(recommendType, response); + } + else if (recommendType == RecommendType.CHART_ARTIST) { + var response = appleMusicFeignClient.getAlbumCharts("albums", recommendType.getLimit()); + return RecommendCategoryDto.ofAppleMusicResponseDto(recommendType, response); + } + else { + throw new BusinessException(CommonErrorCode.UNSUPPORTED_TYPE); + } } } From a178ae06ac0377d6d51bf17e8830b135cc93299a Mon Sep 17 00:00:00 2001 From: siyeonSon Date: Sun, 22 Sep 2024 16:38:58 +0900 Subject: [PATCH 10/16] :sparkles: refactor(api): refactor query of findRecentSongs() --- .../song/repository/SongRepositoryImpl.java | 25 +++++++++++-------- .../dto/response/MusicInfoResponseDto.java | 11 +++++--- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepositoryImpl.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepositoryImpl.java index ff395fea..d3bcbcd6 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepositoryImpl.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepositoryImpl.java @@ -2,6 +2,7 @@ import com.depromeet.domains.recommend.dto.response.MusicInfoResponseDto; import com.querydsl.core.types.Projections; +import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; @@ -12,6 +13,8 @@ import static com.depromeet.music.album.QAlbum.album; import static com.depromeet.music.album.QAlbumCover.albumCover; import static com.depromeet.music.artist.QArtist.artist; +import static com.depromeet.music.genre.QGenre.genre; +import static com.depromeet.music.genre.QSongGenre.songGenre; import static com.depromeet.music.song.QSong.song; @Repository @@ -23,24 +26,26 @@ public class SongRepositoryImpl implements QueryDslSongRepository { @Override public List findRecentSongs(int count) { return queryFactory.select( - Projections.fields( + Projections.constructor( MusicInfoResponseDto.class, album.name, artist.name, song.name, albumCover.albumImage, albumCover.albumThumbnail, - song.genres + JPAExpressions + .select(genre.name) + .from(songGenre) + .join(songGenre.genre, genre) + .where(songGenre.song.eq(song)) + .groupBy(songGenre.song) + .fetchAll() )) .from(item) - .join(item.song, song) - .on(item.song.id.eq(song.id)) - .join(song.album, album) - .on(song.album.id.eq(album.id)) - .join(album.artist, artist) - .on(album.artist.id.eq(artist.id)) - .join(album.albumCover, albumCover) - .on(album.albumCover.id.eq(albumCover.id)) + .join(item.song, song).on(item.song.id.eq(song.id)) + .join(song.album, album).on(song.album.id.eq(album.id)) + .join(album.artist, artist).on(album.artist.id.eq(artist.id)) + .join(album.albumCover, albumCover).on(album.albumCover.id.eq(albumCover.id)) .orderBy(item.createdAt.desc()) .limit(count) .fetch(); diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoResponseDto.java index 5938f747..b01e96a4 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoResponseDto.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoResponseDto.java @@ -1,7 +1,11 @@ package com.depromeet.domains.recommend.dto.response; import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicSongChartResponseDto; -import com.depromeet.music.genre.Genre; +import com.depromeet.music.album.Album; +import com.depromeet.music.album.AlbumCover; +import com.depromeet.music.genre.SongGenre; +import com.depromeet.music.song.Song; +import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @@ -11,6 +15,7 @@ @Getter @NoArgsConstructor +@AllArgsConstructor public class MusicInfoResponseDto { private String albumName; private String artistName; @@ -20,14 +25,14 @@ public class MusicInfoResponseDto { private String albumThumbnailImage; private List genre; - public MusicInfoResponseDto(String albumName, String artistName, String songName, String albumImage, String albumThumbnailImage, List genre) { + public MusicInfoResponseDto(String albumName, String artistName, String songName, String albumImage, String albumThumbnailImage, List genre) { this.albumName = albumName; this.artistName = artistName; this.songName = songName; this.durationTime = ""; // TODO: DB에서 가져오는 음악 데이터는 durationTime이 없음 this.albumImage = albumImage; this.albumThumbnailImage = albumThumbnailImage; - this.genre = genre.stream().map(Genre::getName).toList(); + this.genre = genre; } public static MusicInfoResponseDto fromAppleMusicResponse(AppleMusicSongChartResponseDto.SongData data) { From 3b72796e453c5b4be2941bd52fda8013d35f42b2 Mon Sep 17 00:00:00 2001 From: siyeonSon Date: Sun, 22 Sep 2024 17:01:05 +0900 Subject: [PATCH 11/16] :sparkles: refactor(api): refactor query of findRecentSongs() with JPQL --- .../domains/music/service/MusicService.java | 6 ++++- .../music/song/repository/SongRepository.java | 8 +++++- .../dto/response/MusicInfoResponseDto.java | 25 +++++++++++-------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java index a1b52899..1a994115 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java @@ -8,6 +8,7 @@ import com.depromeet.domains.music.dto.response.MusicResponseDto; import com.depromeet.domains.music.event.CreateSongGenreEvent; import com.depromeet.domains.music.song.repository.SongRepository; +import com.depromeet.domains.recommend.dto.response.MusicInfoResponseDto; import com.depromeet.domains.recommend.dto.response.RecommendCategoryDto; import com.depromeet.domains.recommend.constant.RecommendType; import com.depromeet.music.album.Album; @@ -126,6 +127,9 @@ public MusicResponseDto getMusic(Long songId) { @Transactional(readOnly = true) public RecommendCategoryDto getRecentMusic(RecommendType recommendType) { var recentSongs = songRepository.findRecentSongs(recommendType.getLimit()); - return RecommendCategoryDto.ofMusicInfoResponseDto(recommendType, recentSongs); + List musicInfoResponseDtos = recentSongs.stream() + .map(MusicInfoResponseDto::ofSong) + .toList(); + return RecommendCategoryDto.ofMusicInfoResponseDto(recommendType, musicInfoResponseDtos); } } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepository.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepository.java index 802c77df..a757d600 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepository.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepository.java @@ -5,9 +5,10 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import java.util.List; import java.util.Optional; -public interface SongRepository extends JpaRepository, QueryDslSongRepository { +public interface SongRepository extends JpaRepository { Optional findSongByNameAndAlbum(String name, Album album); @Query("SELECT s FROM Song s JOIN FETCH s.album " + @@ -16,4 +17,9 @@ public interface SongRepository extends JpaRepository, QueryDslSongR "JOIN FETCH s.album.artist " + "JOIN FETCH s.album.albumCover WHERE s.id = :id") Optional findSongById(Long id); + + @Query("SELECT i.song FROM Item i " + + "ORDER BY i.createdAt DESC") + List findRecentSongs(int count); // JPQL은 LIMIT 절을 지원하지 않음 + } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoResponseDto.java index b01e96a4..04418307 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoResponseDto.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/dto/response/MusicInfoResponseDto.java @@ -1,9 +1,7 @@ package com.depromeet.domains.recommend.dto.response; import com.depromeet.external.applemusic.dto.response.catalogchart.AppleMusicSongChartResponseDto; -import com.depromeet.music.album.Album; -import com.depromeet.music.album.AlbumCover; -import com.depromeet.music.genre.SongGenre; +import com.depromeet.music.genre.Genre; import com.depromeet.music.song.Song; import lombok.AllArgsConstructor; import lombok.Getter; @@ -25,14 +23,19 @@ public class MusicInfoResponseDto { private String albumThumbnailImage; private List genre; - public MusicInfoResponseDto(String albumName, String artistName, String songName, String albumImage, String albumThumbnailImage, List genre) { - this.albumName = albumName; - this.artistName = artistName; - this.songName = songName; - this.durationTime = ""; // TODO: DB에서 가져오는 음악 데이터는 durationTime이 없음 - this.albumImage = albumImage; - this.albumThumbnailImage = albumThumbnailImage; - this.genre = genre; + public static MusicInfoResponseDto ofSong(Song song) { + return new MusicInfoResponseDto( + song.getAlbum().getName(), + song.getAlbum().getArtist().getName(), + song.getName(), + "", + song.getAlbum().getAlbumCover().getAlbumImage(), + song.getAlbum().getAlbumCover().getAlbumThumbnail(), + song.getGenres() + .stream() + .map(Genre::getName) + .toList() + ); } public static MusicInfoResponseDto fromAppleMusicResponse(AppleMusicSongChartResponseDto.SongData data) { From bcac59c457f721d19c95c74b96ea11028da24234 Mon Sep 17 00:00:00 2001 From: siyeonSon Date: Mon, 23 Sep 2024 11:41:43 +0900 Subject: [PATCH 12/16] :sparkles: refactor(api): get only one duplicate music --- .../repository/QueryDslSongRepository.java | 9 ---- .../music/song/repository/SongRepository.java | 12 +++-- .../song/repository/SongRepositoryImpl.java | 54 ------------------- 3 files changed, 9 insertions(+), 66 deletions(-) delete mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/QueryDslSongRepository.java delete mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepositoryImpl.java diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/QueryDslSongRepository.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/QueryDslSongRepository.java deleted file mode 100644 index e56a60d5..00000000 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/QueryDslSongRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.depromeet.domains.music.song.repository; - -import com.depromeet.domains.recommend.dto.response.MusicInfoResponseDto; - -import java.util.List; - -public interface QueryDslSongRepository { - List findRecentSongs(int count); -} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepository.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepository.java index a757d600..2a386798 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepository.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepository.java @@ -4,6 +4,7 @@ import com.depromeet.music.song.Song; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import java.util.List; import java.util.Optional; @@ -18,8 +19,13 @@ public interface SongRepository extends JpaRepository { "JOIN FETCH s.album.albumCover WHERE s.id = :id") Optional findSongById(Long id); - @Query("SELECT i.song FROM Item i " + - "ORDER BY i.createdAt DESC") - List findRecentSongs(int count); // JPQL은 LIMIT 절을 지원하지 않음 + @Query(nativeQuery = true, + value = "SELECT DISTINCT s.* FROM (" + + " SELECT s.* FROM song s " + + " JOIN item i ON s.song_id = i.song_id " + + " ORDER BY i.created_at DESC " + + " LIMIT :count" + + ") s") + List findRecentSongs(@Param("count") int count); } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepositoryImpl.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepositoryImpl.java deleted file mode 100644 index d3bcbcd6..00000000 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/song/repository/SongRepositoryImpl.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.depromeet.domains.music.song.repository; - -import com.depromeet.domains.recommend.dto.response.MusicInfoResponseDto; -import com.querydsl.core.types.Projections; -import com.querydsl.jpa.JPAExpressions; -import com.querydsl.jpa.impl.JPAQueryFactory; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -import java.util.List; - -import static com.depromeet.item.QItem.item; -import static com.depromeet.music.album.QAlbum.album; -import static com.depromeet.music.album.QAlbumCover.albumCover; -import static com.depromeet.music.artist.QArtist.artist; -import static com.depromeet.music.genre.QGenre.genre; -import static com.depromeet.music.genre.QSongGenre.songGenre; -import static com.depromeet.music.song.QSong.song; - -@Repository -@RequiredArgsConstructor -public class SongRepositoryImpl implements QueryDslSongRepository { - - private final JPAQueryFactory queryFactory; - - @Override - public List findRecentSongs(int count) { - return queryFactory.select( - Projections.constructor( - MusicInfoResponseDto.class, - album.name, - artist.name, - song.name, - albumCover.albumImage, - albumCover.albumThumbnail, - JPAExpressions - .select(genre.name) - .from(songGenre) - .join(songGenre.genre, genre) - .where(songGenre.song.eq(song)) - .groupBy(songGenre.song) - .fetchAll() - )) - .from(item) - .join(item.song, song).on(item.song.id.eq(song.id)) - .join(song.album, album).on(song.album.id.eq(album.id)) - .join(album.artist, artist).on(album.artist.id.eq(artist.id)) - .join(album.albumCover, albumCover).on(album.albumCover.id.eq(albumCover.id)) - .orderBy(item.createdAt.desc()) - .limit(count) - .fetch(); - } - -} From 8e5c57c88f144a8ce527711f29abb643b8a22983 Mon Sep 17 00:00:00 2001 From: siyeonSon Date: Mon, 23 Sep 2024 11:48:45 +0900 Subject: [PATCH 13/16] :sparkles: refactor(api): change RecommendType name CHART_SONGS -> POPULAR_CHART_SONG --- .../com/depromeet/domains/recommend/constant/RecommendType.java | 2 +- .../domains/recommend/service/SearchRecommendService.java | 2 +- .../external/applemusic/service/AppleMusicService.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/constant/RecommendType.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/constant/RecommendType.java index 7045040a..2ec52c60 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/constant/RecommendType.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/constant/RecommendType.java @@ -6,7 +6,7 @@ @Getter @AllArgsConstructor public enum RecommendType { - CHART_SONGS("인기 있는 음악", 30, true), + POPULAR_CHART_SONG("인기 있는 음악", 30, true), RECENT_SONGS("많이 드랍된 음악", 15, true), CHART_ARTIST("아티스트", 10, false); diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java index d109a6b5..76844efd 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java @@ -42,7 +42,7 @@ public SearchTermRecommendResponseDto recommendSearchTerm() { public RecommendResponseDto recommendSearchSongs() { return new RecommendResponseDto( List.of( - appleMusicService.getCategoryChart(RecommendType.CHART_SONGS), + appleMusicService.getCategoryChart(RecommendType.POPULAR_CHART_SONG), musicService.getRecentMusic(RecommendType.RECENT_SONGS), appleMusicService.getCategoryChart(RecommendType.CHART_ARTIST) ) diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java index bf9a914d..332a354a 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java @@ -15,7 +15,7 @@ public class AppleMusicService { private final AppleMusicFeignClient appleMusicFeignClient; public RecommendCategoryDto getCategoryChart(RecommendType recommendType) { - if (recommendType == RecommendType.CHART_SONGS) { + if (recommendType == RecommendType.POPULAR_CHART_SONG) { var response = appleMusicFeignClient.getSongCharts("songs", recommendType.getLimit()); return RecommendCategoryDto.ofAppleMusicResponseDto(recommendType, response); } From 5438995cbf11ec7f964cf149ef63486d7c491f5c Mon Sep 17 00:00:00 2001 From: siyeonSon Date: Mon, 23 Sep 2024 11:50:06 +0900 Subject: [PATCH 14/16] :sparkles: refactor(api): change RecommendType title according to the design --- .../depromeet/domains/recommend/constant/RecommendType.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/constant/RecommendType.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/constant/RecommendType.java index 2ec52c60..13b155f6 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/constant/RecommendType.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/constant/RecommendType.java @@ -6,8 +6,8 @@ @Getter @AllArgsConstructor public enum RecommendType { - POPULAR_CHART_SONG("인기 있는 음악", 30, true), - RECENT_SONGS("많이 드랍된 음악", 15, true), + POPULAR_CHART_SONG("지금 인기 있는 음악", 30, true), + RECENT_SONGS("최근 드랍된 음악", 15, true), CHART_ARTIST("아티스트", 10, false); private final String title; From 4836bd6274e960ea9f109c09c29c7c1f367c7bd8 Mon Sep 17 00:00:00 2001 From: siyeonSon Date: Mon, 23 Sep 2024 11:55:19 +0900 Subject: [PATCH 15/16] :sparkles: refactor(api): disable logging --- .../domains/recommend/service/SearchRecommendService.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java index 76844efd..ac8d4531 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java @@ -8,12 +8,10 @@ import com.depromeet.domains.recommend.repository.SearchRecommendTermRepository; import com.depromeet.external.applemusic.service.AppleMusicService; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.List; -@Slf4j @Service @RequiredArgsConstructor public class SearchRecommendService { From 70e439a85b211cfbdd965116505bc2b467b3ce28 Mon Sep 17 00:00:00 2001 From: siyeonSon Date: Mon, 23 Sep 2024 11:59:11 +0900 Subject: [PATCH 16/16] :recycle: refactor(api): refactor if-else -> switch case --- .../applemusic/service/AppleMusicService.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java index 332a354a..99c91788 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/applemusic/service/AppleMusicService.java @@ -15,17 +15,17 @@ public class AppleMusicService { private final AppleMusicFeignClient appleMusicFeignClient; public RecommendCategoryDto getCategoryChart(RecommendType recommendType) { - if (recommendType == RecommendType.POPULAR_CHART_SONG) { - var response = appleMusicFeignClient.getSongCharts("songs", recommendType.getLimit()); - return RecommendCategoryDto.ofAppleMusicResponseDto(recommendType, response); - } - else if (recommendType == RecommendType.CHART_ARTIST) { - var response = appleMusicFeignClient.getAlbumCharts("albums", recommendType.getLimit()); - return RecommendCategoryDto.ofAppleMusicResponseDto(recommendType, response); - } - else { - throw new BusinessException(CommonErrorCode.UNSUPPORTED_TYPE); - } + return switch (recommendType) { + case POPULAR_CHART_SONG -> { + var response = appleMusicFeignClient.getSongCharts("songs", recommendType.getLimit()); + yield RecommendCategoryDto.ofAppleMusicResponseDto(recommendType, response); + } + case CHART_ARTIST -> { + var response = appleMusicFeignClient.getAlbumCharts("albums", recommendType.getLimit()); + yield RecommendCategoryDto.ofAppleMusicResponseDto(recommendType, response); + } + default -> throw new BusinessException(CommonErrorCode.UNSUPPORTED_TYPE); + }; } }