Skip to content

Commit e0f49df

Browse files
Merge pull request #307 from prgrms-web-devcourse-final-project/feat/#303
[Artist] 장르별 아티스트 목록 반환
2 parents 2c17662 + 1fab05e commit e0f49df

7 files changed

Lines changed: 82 additions & 47 deletions

File tree

src/main/java/com/back/web7_9_codecrete_be/domain/artists/controller/ArtistsController.java

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -135,28 +135,7 @@ public RsData<LikeArtistResponse> isLiked(
135135
return RsData.success("해당 아티스트 찜 여부 조회 성공", artistService.findArtistLikeByUserId(artist, user));
136136
}
137137

138-
@Operation(summary = "아티스트 인기 순위(구현 전)", description = "Spotify 인기도를 바탕으로 아티스트 인기 순위 랭킹을 제공합니다.")
139-
@GetMapping("/ranking")
140-
public void artistRanking() {}
141-
142-
@Operation(summary = "장르 기반 아티스트 추천(구현 전)", description = "찜한 장르를 기반으로 아티스트 추천 리스트를 제공합니다.")
143-
@GetMapping("/recommendation/{genreId}")
144-
public void recommendArtist(
145-
@PathVariable Long genreId
146-
) {}
147-
148-
@Operation(summary = "공연 셋리스트 생성(구현 전)", description = "사용자가 공연 셋리스트를 생성합니다.")
149-
@PostMapping("/setlist/{concertId}/{artistId}")
150-
public void makeSetlist(
151-
@PathVariable Long concertId,
152-
@PathVariable Long artistId
153-
) {}
154138

155-
@Operation(summary = "공연 셋리스트 조회(구현 전)", description = "다른 사용자들이 생성한 셋리스트를 조회합니다.")
156-
@GetMapping("/setlist/{concertId}/{artistId}")
157-
public void getSetlist(
158-
@PathVariable Long concertId,
159-
@PathVariable Long artistId
160-
) {}
139+
161140
}
162141

src/main/java/com/back/web7_9_codecrete_be/domain/artists/controller/GenreController.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package com.back.web7_9_codecrete_be.domain.artists.controller;
22

3+
import com.back.web7_9_codecrete_be.domain.artists.dto.response.GenreArtistsResponse;
34
import com.back.web7_9_codecrete_be.domain.artists.dto.response.GenreResponse;
5+
import com.back.web7_9_codecrete_be.domain.artists.service.ArtistService;
46
import com.back.web7_9_codecrete_be.domain.artists.service.GenreService;
57
import com.back.web7_9_codecrete_be.global.rsData.RsData;
68
import io.swagger.v3.oas.annotations.Operation;
79
import io.swagger.v3.oas.annotations.tags.Tag;
810
import lombok.RequiredArgsConstructor;
911
import org.springframework.web.bind.annotation.GetMapping;
12+
import org.springframework.web.bind.annotation.PathVariable;
1013
import org.springframework.web.bind.annotation.RequestMapping;
1114
import org.springframework.web.bind.annotation.RestController;
1215

@@ -19,10 +22,19 @@
1922
public class GenreController {
2023

2124
private final GenreService genreService;
25+
private final ArtistService artistService;
2226

2327
@Operation(summary = "전체 장르 목록", description = "DB에 저장되어있는 전체 장르 목록을 반환합니다.")
2428
@GetMapping()
2529
public RsData<List<GenreResponse>> genreList() {
2630
return RsData.success("전체 장르 조회 성공", genreService.genreList());
2731
}
32+
33+
@Operation(summary = "장르별 아티스트 목록", description = "각 장르에 해당하는 아티스트 목록을 반환합니다.")
34+
@GetMapping("/{genreId}")
35+
public RsData<List<GenreArtistsResponse>> recommendArtist(
36+
@PathVariable Long genreId
37+
) {
38+
return RsData.success("해당 장르 아티스트 목록 조회 성공", artistService.findArtistsByGenreId(genreId));
39+
}
2840
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.back.web7_9_codecrete_be.domain.artists.dto.response;
2+
3+
import com.back.web7_9_codecrete_be.domain.artists.entity.Artist;
4+
import com.back.web7_9_codecrete_be.domain.artists.entity.Genre;
5+
import io.swagger.v3.oas.annotations.media.Schema;
6+
7+
public record GenreArtistsResponse(
8+
@Schema(description = "아티스트 id 입니다.")
9+
Long id,
10+
11+
@Schema(description = "아티스트 이름입니다.")
12+
String artistName,
13+
14+
@Schema(description = "한국어 기준 아티스트 이름입니다.")
15+
String nameKo,
16+
17+
@Schema(description = "아티스트 사진 URL 입니다.")
18+
String imageUrl,
19+
20+
@Schema(description = "아티스트의 Spotify id 입니다.")
21+
String spotifyArtistId
22+
) {
23+
public static GenreArtistsResponse from(Artist artist) {
24+
return new GenreArtistsResponse(
25+
artist.getId(),
26+
artist.getArtistName(),
27+
artist.getNameKo(),
28+
artist.getImageUrl(),
29+
artist.getSpotifyArtistId()
30+
);
31+
}
32+
}

src/main/java/com/back/web7_9_codecrete_be/domain/artists/repository/ArtistRepository.java

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.back.web7_9_codecrete_be.domain.artists.repository;
22

33
import com.back.web7_9_codecrete_be.domain.artists.entity.Artist;
4+
import com.back.web7_9_codecrete_be.domain.artists.entity.ArtistType;
45
import org.springframework.data.domain.Pageable;
56
import org.springframework.data.domain.Slice;
67
import org.springframework.data.jpa.repository.JpaRepository;
@@ -12,8 +13,6 @@
1213

1314
@Repository
1415
public interface ArtistRepository extends JpaRepository<Artist, Long> {
15-
boolean existsBySpotifyArtistId(String spotifyArtistId);
16-
java.util.Optional<Artist> findBySpotifyArtistId(String spotifyArtistId);
1716

1817
// 아티스트 상세 조회용 - artistGenres와 genre를 fetch join하여 N+1 문제 방지
1918
@Query("""
@@ -33,36 +32,40 @@ public interface ArtistRepository extends JpaRepository<Artist, Long> {
3332
boolean existsByArtistName(String artistName);
3433
boolean existsByNameKo(String nameKo);
3534

36-
/**
37-
* 같은 artistGroup인 아티스트들 조회 (관련 아티스트 추천용)
38-
* artistGenres를 fetch join하여 N+1 문제 방지
39-
*/
35+
// 같은 artistGroup인 아티스트들 조회 (관련 아티스트 추천용) - artistGenres를 fetch join하여 N+1 문제 방지
4036
@Query("""
4137
SELECT DISTINCT a FROM Artist a
4238
LEFT JOIN FETCH a.artistGenres ag
4339
LEFT JOIN FETCH ag.genre
4440
WHERE a.artistGroup = :artistGroup AND a.id != :excludeId
4541
ORDER BY a.likeCount DESC, a.id ASC
4642
""")
47-
List<Artist> findByArtistGroupAndIdNot(@org.springframework.data.repository.query.Param("artistGroup") String artistGroup,
48-
@org.springframework.data.repository.query.Param("excludeId") long excludeId,
43+
List<Artist> findByArtistGroupAndIdNot(@Param("artistGroup") String artistGroup,
44+
@Param("excludeId") long excludeId,
4945
Pageable pageable);
5046

51-
/**
52-
* 같은 genre인 아티스트들 조회 (관련 아티스트 추천용)
53-
* artistGenres와 genre를 fetch join하여 N+1 문제 방지
54-
*/
47+
// 같은 genre인 아티스트들 조회 (관련 아티스트 추천용) - artistGenres와 genre를 fetch join하여 N+1 문제 방지
5548
@Query("""
5649
SELECT DISTINCT a FROM Artist a
5750
JOIN FETCH a.artistGenres ag
5851
JOIN FETCH ag.genre
5952
WHERE ag.genre.id = :genreId AND a.id != :excludeId
6053
ORDER BY a.likeCount DESC, a.id ASC
6154
""")
62-
List<Artist> findByGenreIdAndIdNot(@org.springframework.data.repository.query.Param("genreId") Long genreId,
63-
@org.springframework.data.repository.query.Param("excludeId") long excludeId,
55+
List<Artist> findByGenreIdAndIdNot(@Param("genreId") Long genreId,
56+
@Param("excludeId") long excludeId,
6457
Pageable pageable);
6558

59+
// 장르별 아티스트 목록 조회 - artistGenres와 genre를 fetch join하여 N+1 문제 방지
60+
@Query("""
61+
SELECT DISTINCT a FROM Artist a
62+
JOIN FETCH a.artistGenres ag
63+
JOIN FETCH ag.genre g
64+
WHERE g.id = :genreId
65+
ORDER BY a.likeCount DESC, a.id ASC
66+
""")
67+
List<Artist> findArtistsByGenreId(@Param("genreId") Long genreId);
68+
6669
List<Artist> findAllByArtistNameContainingIgnoreCaseOrNameKoContainingIgnoreCase(String artistName1, String artistName2);
6770

6871
Slice<Artist> findAllBy(Pageable pageable);
@@ -84,24 +87,21 @@ List<Artist> findByGenreIdAndIdNot(@org.springframework.data.repository.query.Pa
8487

8588
// 배치 조회: spotifyId 리스트로 존재하는 아티스트의 spotifyId만 반환
8689
@Query("SELECT a.spotifyArtistId FROM Artist a WHERE a.spotifyArtistId IN :spotifyIds")
87-
List<String> findSpotifyIdsBySpotifyIdsIn(@org.springframework.data.repository.query.Param("spotifyIds") List<String> spotifyIds);
90+
List<String> findSpotifyIdsBySpotifyIdsIn(@Param("spotifyIds") List<String> spotifyIds);
8891

8992
// 배치 조회: spotifyId 리스트로 존재하는 아티스트 전체 엔티티 반환 (Bulk 저장용)
9093
@Query("SELECT a FROM Artist a WHERE a.spotifyArtistId IN :spotifyIds")
91-
List<Artist> findBySpotifyArtistIdIn(@org.springframework.data.repository.query.Param("spotifyIds") List<String> spotifyIds);
94+
List<Artist> findBySpotifyArtistIdIn(@Param("spotifyIds") List<String> spotifyIds);
9295

93-
/**
94-
* 같은 artistType인 아티스트들 조회 (관련 아티스트 추천용, fallback)
95-
* artistGenres를 fetch join하여 N+1 문제 방지
96-
*/
96+
// 같은 artistType인 아티스트들 조회 (관련 아티스트 추천용, fallback) - artistGenres를 fetch join하여 N+1 문제 방지
9797
@Query("""
9898
SELECT DISTINCT a FROM Artist a
9999
LEFT JOIN FETCH a.artistGenres ag
100100
LEFT JOIN FETCH ag.genre
101101
WHERE a.artistType = :artistType AND a.id != :excludeId
102102
ORDER BY a.likeCount DESC, a.id ASC
103103
""")
104-
List<Artist> findByArtistTypeAndIdNot(@org.springframework.data.repository.query.Param("artistType") com.back.web7_9_codecrete_be.domain.artists.entity.ArtistType artistType,
105-
@org.springframework.data.repository.query.Param("excludeId") long excludeId,
104+
List<Artist> findByArtistTypeAndIdNot(@Param("artistType") ArtistType artistType,
105+
@Param("excludeId") long excludeId,
106106
Pageable pageable);
107107
}

src/main/java/com/back/web7_9_codecrete_be/domain/artists/service/ArtistService.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@
77
import com.back.web7_9_codecrete_be.domain.artists.repository.ArtistRepository;
88
import com.back.web7_9_codecrete_be.domain.artists.repository.ArtistLikeRepository;
99
import com.back.web7_9_codecrete_be.domain.artists.repository.ConcertArtistRepository;
10+
import com.back.web7_9_codecrete_be.domain.artists.repository.GenreRepository;
11+
import com.back.web7_9_codecrete_be.domain.artists.service.GenreService;
1012
import com.back.web7_9_codecrete_be.domain.concerts.entity.Concert;
1113
import com.back.web7_9_codecrete_be.domain.concerts.repository.ConcertRepository;
1214
import com.back.web7_9_codecrete_be.domain.concerts.service.ConcertService;
1315
import com.back.web7_9_codecrete_be.domain.users.entity.User;
1416
import com.back.web7_9_codecrete_be.global.error.code.ArtistErrorCode;
17+
import com.back.web7_9_codecrete_be.global.error.code.GenreErrorCode;
1518
import com.back.web7_9_codecrete_be.global.error.exception.BusinessException;
1619
import lombok.AccessLevel;
1720
import org.springframework.data.domain.PageRequest;
@@ -31,6 +34,7 @@ public class ArtistService {
3134

3235
private final SpotifyService spotifyService;
3336
private final ArtistRepository artistRepository;
37+
private final GenreRepository genreRepository;
3438
private final GenreService genreService;
3539
private final ArtistLikeRepository artistLikeRepository;
3640
private final ConcertArtistRepository concertArtistRepository;
@@ -197,7 +201,6 @@ public void deleteLikeArtist(Long artistId, User user) {
197201
@Transactional
198202
public void linkArtistConcert(Long artistId, Long concertId) {
199203
Artist artist = findArtist(artistId);
200-
// TODO: 추후 수정 예정
201204
Concert concert = concertRepository.findById(concertId)
202205
.orElseThrow();
203206
concertArtistRepository.save(new ConcertArtist(artist, concert));
@@ -231,4 +234,14 @@ public List<LikeArtistsResponse> findLikeArtistsByUserid(User user) {
231234
.toList();
232235
}
233236

237+
@Transactional(readOnly = true)
238+
public List<GenreArtistsResponse> findArtistsByGenreId(Long genreId) {
239+
if (!genreRepository.existsById(genreId)) {
240+
throw new BusinessException(GenreErrorCode.GENRE_NOT_FOUND);
241+
}
242+
List<Artist> artists = artistRepository.findArtistsByGenreId(genreId);
243+
return artists.stream()
244+
.map(GenreArtistsResponse::from)
245+
.toList();
246+
}
234247
}

src/main/java/com/back/web7_9_codecrete_be/domain/artists/service/GenreService.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package com.back.web7_9_codecrete_be.domain.artists.service;
22

3-
import com.back.web7_9_codecrete_be.domain.artists.dto.response.ConcertListByArtistResponse;
43
import com.back.web7_9_codecrete_be.domain.artists.dto.response.GenreResponse;
54
import com.back.web7_9_codecrete_be.domain.artists.entity.Genre;
65
import com.back.web7_9_codecrete_be.domain.artists.repository.GenreRepository;
7-
import com.back.web7_9_codecrete_be.domain.concerts.entity.Concert;
86
import com.back.web7_9_codecrete_be.global.error.code.GenreErrorCode;
97
import com.back.web7_9_codecrete_be.global.error.exception.BusinessException;
108
import lombok.RequiredArgsConstructor;

src/main/java/com/back/web7_9_codecrete_be/global/security/SecurityConfig.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
5959
"/api/v1/location/**", //location 정보 조회 도메인(임시)
6060
"/api/v1/concerts/**", // concert 정보 조회 도메인
6161
"/api/v1/artists/**", // artist 정보 저장 도메인(임시)
62+
"/api/v1/genre/**",
6263
"/api/v1/users/**",
6364
"/api/v1/chats/**",
6465
"/api/v1/reviews/**",

0 commit comments

Comments
 (0)