Skip to content

Commit 3cb399f

Browse files
Merge pull request #188 from prgrms-web-devcourse-final-project/bug/#180
[Artist] 장르 정보 보완
2 parents 246e8e1 + c26bd69 commit 3cb399f

10 files changed

Lines changed: 233 additions & 60 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public RsData<Integer> fetchMusicBrainzIds(
6262
public RsData<Void> create(
6363
@Valid @RequestBody CreateRequest reqBody
6464
) {
65-
artistService.createArtist(reqBody.artistName(), reqBody.artistGroup(), reqBody.artistType(), reqBody.genreName());
65+
artistService.createArtist(reqBody.spotifyID(), reqBody.artistName(), reqBody.artistGroup(), reqBody.artistType(), reqBody.genreName());
6666
return RsData.success("아티스트 생성이 완료되었습니다.", null);
6767
}
6868

src/main/java/com/back/web7_9_codecrete_be/domain/artists/dto/request/CreateRequest.java

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

33
import com.back.web7_9_codecrete_be.domain.artists.entity.ArtistType;
4+
import com.back.web7_9_codecrete_be.domain.artists.entity.Genre;
45
import io.swagger.v3.oas.annotations.media.Schema;
56
import jakarta.validation.constraints.NotBlank;
67
import jakarta.validation.constraints.NotNull;
78
import jakarta.validation.constraints.Size;
89

910
public record CreateRequest(
11+
@NotBlank
12+
@Size(max = 30, message = "Spotify ID 는 필수로 입력해야합니다.")
13+
@Schema(description = "Spotify ID 입니다.")
14+
String spotifyID,
15+
1016
@NotBlank(message = "아티스트 이름은 필수로 입력해야합니다.")
1117
@Size(max = 200, message = "아티스트 이름은 200자를 넘길 수 없습니다.")
1218
@Schema(description = "아티스트 이름입니다.")

src/main/java/com/back/web7_9_codecrete_be/domain/artists/dto/response/ArtistListResponse.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import com.back.web7_9_codecrete_be.domain.artists.entity.Artist;
44
import io.swagger.v3.oas.annotations.media.Schema;
55

6+
import java.util.List;
7+
68
public record ArtistListResponse(
79
@Schema(description = "아티스트 아이디입니다.")
810
Long id,
@@ -13,8 +15,8 @@ public record ArtistListResponse(
1315
@Schema(description = "아티스트 소속 그룹입니다. 아티스트 이름이 그룹인 경우, null 로 처리됩니다.")
1416
String artistGroup,
1517

16-
@Schema(description = "장르 이름입니다.")
17-
String genreName,
18+
@Schema(description = "장르입니다.")
19+
List<String> genres,
1820

1921
@Schema(description = "받은 좋아요 수(찜한 사람 수) 입니다.")
2022
int likeCount,
@@ -25,24 +27,33 @@ public record ArtistListResponse(
2527
@Schema(description = "로그인한 유저의 좋아요 여부입니다. 비회원인 경우 false입니다.")
2628
Boolean isLiked
2729
) {
30+
public static List<String> getGenre(Artist artist) {
31+
return artist.getArtistGenres().stream()
32+
.map(ag -> ag.getGenre().getGenreName())
33+
.distinct()
34+
.toList();
35+
}
36+
2837
public static ArtistListResponse from(Artist artist) {
38+
List<String> genres = getGenre(artist);
2939
return new ArtistListResponse(
3040
artist.getId(),
3141
artist.getArtistName(),
3242
artist.getArtistGroup(),
33-
artist.getGenre().getGenreName(),
43+
genres,
3444
artist.getLikeCount(),
3545
artist.getImageUrl(),
3646
false // 기본값은 false
3747
);
3848
}
3949

4050
public static ArtistListResponse from(Artist artist, boolean isLiked) {
51+
List<String> genres = getGenre(artist);
4152
return new ArtistListResponse(
4253
artist.getId(),
4354
artist.getArtistName(),
4455
artist.getArtistGroup(),
45-
artist.getGenre().getGenreName(),
56+
genres,
4657
artist.getLikeCount(),
4758
artist.getImageUrl(),
4859
isLiked

src/main/java/com/back/web7_9_codecrete_be/domain/artists/entity/Artist.java

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import lombok.NoArgsConstructor;
77
import lombok.Setter;
88

9+
import java.util.HashSet;
10+
import java.util.Set;
11+
912

1013
@Entity
1114
@Getter
@@ -28,9 +31,6 @@ public class Artist {
2831
@Column(name = "artist_type")
2932
private ArtistType artistType;
3033

31-
@ManyToOne(fetch = FetchType.LAZY)
32-
private Genre genre;
33-
3434
@Column(name = "spotify_artist_id", unique = true)
3535
private String spotifyArtistId;
3636

@@ -46,19 +46,25 @@ public class Artist {
4646
@Column(name = "image_url")
4747
private String imageUrl;
4848

49+
@OneToMany(mappedBy = "artist", cascade = CascadeType.ALL, orphanRemoval = true)
50+
private Set<ArtistGenre> artistGenres = new HashSet<>();
51+
4952
public Artist(String spotifyArtistId, String artistName, String artistGroup, ArtistType artistType, Genre genre) {
5053
this.spotifyArtistId = spotifyArtistId;
5154
this.artistName = artistName;
5255
this.artistGroup = artistGroup; // 옵션 B: seed에서는 null
5356
this.artistType = artistType; // 옵션 B: seed에서는 "SINGER"
54-
this.genre = genre;
57+
if (genre != null) {
58+
addGenre(genre);
59+
}
5560
}
5661

57-
public Artist(String artistName, String artistGroup, ArtistType artistType, Genre genre) {
62+
// 장르 없이 생성하는 생성자 (시드 데이터용)
63+
public Artist(String spotifyArtistId, String artistName, String artistGroup, ArtistType artistType) {
64+
this.spotifyArtistId = spotifyArtistId;
5865
this.artistName = artistName;
5966
this.artistGroup = artistGroup;
6067
this.artistType = artistType;
61-
this.genre = genre;
6268
}
6369

6470
public void updateProfile(String nameKo, String artistGroup, ArtistType artistType) {
@@ -79,10 +85,6 @@ public void changeType(ArtistType type) {
7985
this.artistType = type;
8086
}
8187

82-
public void changeGenre(Genre genre) {
83-
this.genre = genre;
84-
}
85-
8688
public void increaseLikeCount() {
8789
this.likeCount++;
8890
}
@@ -96,4 +98,16 @@ public void decreaseLikeCount() {
9698
public void setMusicBrainzId(String musicBrainzId) {
9799
this.musicBrainzId = musicBrainzId;
98100
}
101+
102+
public void addGenre(Genre genre) {
103+
this.artistGenres.add(new ArtistGenre(this, genre));
104+
}
105+
106+
public void replaceGenres(Set<Genre> genres) {
107+
this.artistGenres.clear(); // orphanRemoval=true라 매핑 row 삭제됨
108+
for (Genre genre : genres) {
109+
this.artistGenres.add(new ArtistGenre(this, genre));
110+
}
111+
}
112+
99113
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.back.web7_9_codecrete_be.domain.artists.entity;
2+
3+
import jakarta.persistence.*;
4+
import lombok.AccessLevel;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
8+
@Entity
9+
@Getter
10+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
11+
public class ArtistGenre {
12+
@Id
13+
@GeneratedValue(strategy = GenerationType.IDENTITY)
14+
@Column(name = "artist_genre_id")
15+
private Long id;
16+
17+
@ManyToOne(fetch = FetchType.LAZY)
18+
@JoinColumn(name = "artist_id", nullable = false)
19+
private Artist artist;
20+
21+
@ManyToOne(fetch = FetchType.LAZY)
22+
@JoinColumn(name = "genre_id", nullable = false)
23+
private Genre genre;
24+
25+
public ArtistGenre(Artist artist, Genre genre) {
26+
this.artist = artist;
27+
this.genre = genre;
28+
}
29+
}

src/main/java/com/back/web7_9_codecrete_be/domain/artists/entity/Genre.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,8 @@ public class Genre {
1717
@Column(name = "genre_name", nullable = false, length = 30)
1818
private String genreName;
1919

20-
@Column(name = "genre_group", length = 30)
21-
private String genreGroup;
2220

23-
@Column(name = "genre_memo")
24-
private String genreMemo;
25-
26-
public Genre(String genreName, String genreGroup, String genreMemo) {
21+
public Genre(String genreName) {
2722
this.genreName = genreName;
28-
this.genreGroup = genreGroup;
29-
this.genreMemo = genreMemo;
3023
}
3124
}

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
@Repository
1313
public interface ArtistRepository extends JpaRepository<Artist, Long> {
1414
boolean existsBySpotifyArtistId(String spotifyArtistId);
15+
java.util.Optional<Artist> findBySpotifyArtistId(String spotifyArtistId);
1516

1617
@Query("SELECT a FROM Artist a WHERE a.nameKo IS NULL ORDER BY a.id ASC")
1718
List<Artist> findByNameKoIsNullOrderByIdAsc(Pageable pageable);
@@ -23,7 +24,16 @@ public interface ArtistRepository extends JpaRepository<Artist, Long> {
2324
boolean existsByNameKo(String nameKo);
2425

2526
List<Artist> findTop5ByArtistGroupAndIdNot(String artistGroup, long excludeId);
26-
List<Artist> findTop5ByGenreIdAndIdNot(Long genreId, long excludeId);
27+
28+
@Query("""
29+
SELECT DISTINCT a FROM Artist a
30+
JOIN a.artistGenres ag
31+
WHERE ag.genre.id = :genreId AND a.id != :excludeId
32+
ORDER BY a.likeCount DESC
33+
""")
34+
List<Artist> findTop5ByGenreIdAndIdNot(@org.springframework.data.repository.query.Param("genreId") Long genreId,
35+
@org.springframework.data.repository.query.Param("excludeId") long excludeId,
36+
Pageable pageable);
2737

2838
List<Artist> findAllByArtistNameContainingIgnoreCaseOrNameKoContainingIgnoreCase(String artistName1, String artistName2);
2939

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
import org.springframework.data.jpa.repository.JpaRepository;
55
import org.springframework.stereotype.Repository;
66

7+
import java.util.List;
78
import java.util.Optional;
89

910
@Repository
1011
public interface GenreRepository extends JpaRepository<Genre, Long> {
1112
Optional<Genre> findByGenreName(String genreName);
13+
List<Genre> findByGenreNameIn(List<String> genreNames);
1214
}

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ public int setArtist() {
5252
}
5353

5454
@Transactional
55-
public Artist createArtist(String artistName, String artistGroup, ArtistType artistType, String genreName) {
55+
public Artist createArtist(String spotifyArtistId, String artistName, String artistGroup, ArtistType artistType, String genreName) {
5656
Genre genre = genreService.findByGenreName(genreName);
5757
if(artistRepository.existsByArtistName(artistName) || artistRepository.existsByNameKo(artistName)) {
5858
throw new BusinessException(ArtistErrorCode.ARTIST_ALREADY_EXISTS);
5959
}
60-
Artist artist = new Artist(artistName, artistGroup, artistType, genre);
60+
Artist artist = new Artist(spotifyArtistId, artistName, artistGroup, artistType, genre);
6161
artistRepository.save(artist);
6262
return artist;
6363
}
@@ -114,13 +114,19 @@ public ArtistDetailResponse getArtistDetail(Long artistId, User user) {
114114
isLiked = artistLikeRepository.existsByArtistAndUser(artist, user);
115115
}
116116

117+
// 첫 번째 장르 ID 가져오기 (없으면 null)
118+
Long genreId = artist.getArtistGenres().stream()
119+
.findFirst()
120+
.map(ag -> ag.getGenre().getId())
121+
.orElse(null);
122+
117123
return spotifyService.getArtistDetail(
118124
artist.getSpotifyArtistId(),
119125
artist.getArtistGroup(),
120126
artist.getArtistType(),
121127
likeCount,
122128
artist.getId(),
123-
artist.getGenre() != null ? artist.getGenre().getId() : null,
129+
genreId,
124130
isLiked
125131
);
126132
}
@@ -149,7 +155,7 @@ public void updateArtist(Long id, UpdateRequest req) {
149155

150156
if (req.genreName() != null && !req.genreName().isBlank()) {
151157
Genre genre = genreService.findByGenreName(req.genreName().trim());
152-
artist.changeGenre(genre);
158+
artist.replaceGenres(Set.of(genre));
153159
changed = true;
154160
}
155161

0 commit comments

Comments
 (0)