diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/artists/controller/ArtistsController.java b/src/main/java/com/back/web7_9_codecrete_be/domain/artists/controller/ArtistsController.java index b0432528..6d193f83 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/domain/artists/controller/ArtistsController.java +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/artists/controller/ArtistsController.java @@ -43,7 +43,7 @@ public RsData enrich( public RsData create( @RequestBody CreateRequest reqBody ) { - artistService.createArtist(reqBody.artistName(), reqBody.artistGroup(), reqBody.artistGroup(), reqBody.genreName()); + artistService.createArtist(reqBody.artistName(), reqBody.artistGroup(), reqBody.artistType(), reqBody.genreName()); return RsData.success("아티스트 생성이 완료되었습니다.", null); } diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/artists/dto/request/CreateRequest.java b/src/main/java/com/back/web7_9_codecrete_be/domain/artists/dto/request/CreateRequest.java index 4fef288d..e2333605 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/domain/artists/dto/request/CreateRequest.java +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/artists/dto/request/CreateRequest.java @@ -1,9 +1,22 @@ package com.back.web7_9_codecrete_be.domain.artists.dto.request; +import com.back.web7_9_codecrete_be.domain.artists.entity.ArtistType; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + public record CreateRequest( + @NotNull(message = "아티스트 이름은 필수로 입력해야합니다.") + @Size(max = 200, message = "아티스트 이름은 200자를 넘길 수 없습니다.") String artistName, + + @Size(max = 150, message = "아티스트 그룹 이름은 150자를 넘길 수 없습니다.") String artistGroup, - String artistType, + + @NotNull(message = "아티스트 타입은 필수로 입력해야합니다(SOLO or GROUP)") + ArtistType artistType, + + @NotNull(message = "장르는 필수로 입력해야합니다.") + @Size(max = 30, message = "장르는 30자를 넘길 수 없습니다.") String genreName ) { } diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/artists/dto/request/UpdateRequest.java b/src/main/java/com/back/web7_9_codecrete_be/domain/artists/dto/request/UpdateRequest.java index 6e43712f..231ced72 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/domain/artists/dto/request/UpdateRequest.java +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/artists/dto/request/UpdateRequest.java @@ -1,9 +1,18 @@ package com.back.web7_9_codecrete_be.domain.artists.dto.request; +import com.back.web7_9_codecrete_be.domain.artists.entity.ArtistType; +import jakarta.validation.constraints.Size; + public record UpdateRequest( + @Size(max = 200, message = "아티스트 이름은 200자를 넘길 수 없습니다.") String artistName, + + @Size(max = 150, message = "아티스트 그룹 이름은 150자를 넘길 수 없습니다.") String artistGroup, - String artistType, + + ArtistType artistType, + + @Size(max = 30, message = "장르 이름은 30자를 넘길 수 없습니다.") String genreName ) { } diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/artists/dto/response/ArtistDetailResponse.java b/src/main/java/com/back/web7_9_codecrete_be/domain/artists/dto/response/ArtistDetailResponse.java index 91ed37d8..d18f8baa 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/domain/artists/dto/response/ArtistDetailResponse.java +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/artists/dto/response/ArtistDetailResponse.java @@ -1,11 +1,13 @@ package com.back.web7_9_codecrete_be.domain.artists.dto.response; +import com.back.web7_9_codecrete_be.domain.artists.entity.ArtistType; + import java.util.List; public record ArtistDetailResponse( String artistName, String artistGroup, - String artistType, + ArtistType artistType, String profileImageUrl, long likeCount, int totalAlbums, diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/artists/entity/Artist.java b/src/main/java/com/back/web7_9_codecrete_be/domain/artists/entity/Artist.java index 23db1cc3..126ab267 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/domain/artists/entity/Artist.java +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/artists/entity/Artist.java @@ -24,8 +24,9 @@ public class Artist { @Column(name = "artist_group") private String artistGroup; + @Enumerated(EnumType.STRING) @Column(name = "artist_type") - private String artistType; + private ArtistType artistType; @ManyToOne(fetch = FetchType.LAZY) private Genre genre; @@ -36,7 +37,7 @@ public class Artist { @Column(name = "name_ko", length = 200) private String nameKo; - public Artist(String spotifyArtistId, String artistName, String artistGroup, String artistType, Genre genre) { + public Artist(String spotifyArtistId, String artistName, String artistGroup, ArtistType artistType, Genre genre) { this.spotifyArtistId = spotifyArtistId; this.artistName = artistName; this.artistGroup = artistGroup; // 옵션 B: seed에서는 null @@ -44,14 +45,14 @@ public Artist(String spotifyArtistId, String artistName, String artistGroup, Str this.genre = genre; } - public Artist(String artistName, String artistGroup, String artistType, Genre genre) { + public Artist(String artistName, String artistGroup, ArtistType artistType, Genre genre) { this.artistName = artistName; this.artistGroup = artistGroup; this.artistType = artistType; this.genre = genre; } - public void updateProfile(String nameKo, String artistGroup, String artistType) { + public void updateProfile(String nameKo, String artistGroup, ArtistType artistType) { this.nameKo = nameKo; this.artistGroup = artistGroup; // nullable this.artistType = artistType; // "SOLO" / "GROUP" @@ -65,7 +66,7 @@ public void changeGroup(String group) { this.artistGroup = group; } - public void changeType(String type) { + public void changeType(ArtistType type) { this.artistType = type; } diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/artists/entity/ArtistType.java b/src/main/java/com/back/web7_9_codecrete_be/domain/artists/entity/ArtistType.java new file mode 100644 index 00000000..e05c0e83 --- /dev/null +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/artists/entity/ArtistType.java @@ -0,0 +1,6 @@ +package com.back.web7_9_codecrete_be.domain.artists.entity; + +public enum ArtistType { + SOLO, + GROUP +} diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/artists/service/ArtistEnrichService.java b/src/main/java/com/back/web7_9_codecrete_be/domain/artists/service/ArtistEnrichService.java index 8cdc4d04..ae4273d1 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/domain/artists/service/ArtistEnrichService.java +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/artists/service/ArtistEnrichService.java @@ -1,6 +1,7 @@ package com.back.web7_9_codecrete_be.domain.artists.service; import com.back.web7_9_codecrete_be.domain.artists.entity.Artist; +import com.back.web7_9_codecrete_be.domain.artists.entity.ArtistType; import com.back.web7_9_codecrete_be.domain.artists.repository.ArtistRepository; import com.back.web7_9_codecrete_be.global.musicbrainz.MusicBrainzClient; import com.back.web7_9_codecrete_be.global.wikidata.WikidataClient; @@ -95,13 +96,28 @@ protected void enrichSingleArtist(Artist artist) { } // 기존 artistType이 있으면 유지, 없으면 가져온 값 사용 - String artistType = result.artistType != null ? result.artistType : artist.getArtistType(); + String artistTypeStr = result.artistType != null ? result.artistType : + (artist.getArtistType() != null ? artist.getArtistType().name() : null); + + // String을 ArtistType enum으로 변환 + ArtistType artistType; + if (artistTypeStr != null) { + try { + artistType = ArtistType.valueOf(artistTypeStr); + } catch (IllegalArgumentException e) { + log.warn("잘못된 artistType 값: {}, 기본값 SOLO 사용", artistTypeStr); + artistType = ArtistType.SOLO; + } + } else { + // 기존 값이 없고 새 값도 없으면 기본값 사용 + artistType = artist.getArtistType() != null ? artist.getArtistType() : ArtistType.SOLO; + } - // ✅ 기존 row를 "보강" + // 기존 row를 "보강" artist.updateProfile(result.nameKo, result.artistGroup, artistType); // 명시적으로 save하여 변경사항을 DB에 즉시 반영 artistRepository.save(artist); - log.info("✅ Enrich 성공: artistId={}, name={}, nameKo={}, group={}, type={}, source={}", + log.info("Enrich 성공: artistId={}, name={}, nameKo={}, group={}, type={}, source={}", artist.getId(), artist.getArtistName(), result.nameKo, result.artistGroup, artistType, result.source); } diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/artists/service/ArtistService.java b/src/main/java/com/back/web7_9_codecrete_be/domain/artists/service/ArtistService.java index ecda161a..46101738 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/domain/artists/service/ArtistService.java +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/artists/service/ArtistService.java @@ -4,6 +4,7 @@ import com.back.web7_9_codecrete_be.domain.artists.dto.response.ArtistListResponse; import com.back.web7_9_codecrete_be.domain.artists.dto.response.ArtistDetailResponse; import com.back.web7_9_codecrete_be.domain.artists.entity.Artist; +import com.back.web7_9_codecrete_be.domain.artists.entity.ArtistType; import com.back.web7_9_codecrete_be.domain.artists.entity.Genre; import com.back.web7_9_codecrete_be.domain.artists.repository.ArtistRepository; import com.back.web7_9_codecrete_be.domain.artists.repository.ArtistLikeRepository; @@ -30,7 +31,7 @@ public int setArtist() { } @Transactional - public Artist createArtist(String artistName, String artistGroup, String artistType, String genreName) { + public Artist createArtist(String artistName, String artistGroup, ArtistType artistType, String genreName) { Genre genre = genreService.findByGenreName(genreName); if(artistRepository.existsByArtistName(artistName) || artistRepository.existsByNameKo(artistName)) { throw new BusinessException(ArtistErrorCode.ARTIST_ALREADY_EXISTS); @@ -85,8 +86,8 @@ public void updateArtist(Long id, UpdateRequest req) { changed = true; } - if (req.artistType() != null && !req.artistType().isBlank()) { - artist.changeType(req.artistType().trim()); + if (req.artistType() != null) { + artist.changeType(req.artistType()); changed = true; } diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/artists/service/SpotifyService.java b/src/main/java/com/back/web7_9_codecrete_be/domain/artists/service/SpotifyService.java index b80b455c..58c02b70 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/domain/artists/service/SpotifyService.java +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/artists/service/SpotifyService.java @@ -4,6 +4,8 @@ import com.back.web7_9_codecrete_be.domain.artists.dto.response.ArtistDetailResponse; import com.back.web7_9_codecrete_be.domain.artists.dto.response.RelatedArtistResponse; import com.back.web7_9_codecrete_be.domain.artists.dto.response.TopTrackResponse; +import com.back.web7_9_codecrete_be.domain.artists.entity.Artist; +import com.back.web7_9_codecrete_be.domain.artists.entity.ArtistType; import com.back.web7_9_codecrete_be.domain.artists.entity.Genre; import com.back.web7_9_codecrete_be.domain.artists.repository.ArtistRepository; import com.back.web7_9_codecrete_be.domain.artists.repository.GenreRepository; @@ -19,7 +21,6 @@ import se.michaelthelin.spotify.enums.AlbumType; import se.michaelthelin.spotify.exceptions.detailed.NotFoundException; import se.michaelthelin.spotify.model_objects.specification.AlbumSimplified; -import se.michaelthelin.spotify.model_objects.specification.Artist; import se.michaelthelin.spotify.model_objects.specification.Image; import se.michaelthelin.spotify.model_objects.specification.Paging; import se.michaelthelin.spotify.model_objects.specification.Track; @@ -63,7 +64,7 @@ public int seedKoreanArtists300() { int offset = 0; while (totalSaved < targetCount) { - Paging paging = api.searchArtists(q) + Paging paging = api.searchArtists(q) .limit(limit) .offset(offset) .build() @@ -85,10 +86,10 @@ public int seedKoreanArtists300() { String mainGenreName = pickMainGenreName(spotifyArtist); Genre genre = findOrCreateGenreByName(mainGenreName, null); - String artistType = inferArtistType(spotifyArtist); + String artistTypeStr = inferArtistType(spotifyArtist); + ArtistType artistType = ArtistType.valueOf(artistTypeStr); - com.back.web7_9_codecrete_be.domain.artists.entity.Artist artistEntity = - new com.back.web7_9_codecrete_be.domain.artists.entity.Artist( + Artist artistEntity = new Artist( spotifyId, name.trim(), null, // artistGroup @@ -127,7 +128,7 @@ public int seedKoreanArtists300() { "korean indie", "korean rock" ); - private boolean isLikelyKoreanMusic(Artist a) { + private boolean isLikelyKoreanMusic(se.michaelthelin.spotify.model_objects.specification.Artist a) { String[] genres = a.getGenres(); if (genres != null) { for (String g : genres) { @@ -142,7 +143,7 @@ private boolean isLikelyKoreanMusic(Artist a) { return name != null && name.matches(".*[가-힣].*"); } - private String pickMainGenreName(Artist a) { + private String pickMainGenreName(se.michaelthelin.spotify.model_objects.specification.Artist a) { String[] genres = a.getGenres(); if (genres == null || genres.length == 0) return "k-pop"; @@ -161,7 +162,7 @@ private String pickMainGenreName(Artist a) { return "k-pop"; } - private String inferArtistType(Artist a) { + private String inferArtistType(se.michaelthelin.spotify.model_objects.specification.Artist a) { String[] genres = a.getGenres(); if (genres != null) { for (String g : genres) { @@ -182,7 +183,7 @@ private Genre findOrCreateGenreByName(String genreName, String genreGroup) { public ArtistDetailResponse getArtistDetail( String spotifyArtistId, String artistGroup, - String artistType, + ArtistType artistType, long likeCount, long artistId, Long genreId @@ -190,7 +191,7 @@ public ArtistDetailResponse getArtistDetail( try { SpotifyApi api = spotifyClient.getAuthorizedApi(); - Artist artist = api.getArtist(spotifyArtistId).build().execute(); // 메인 정보는 실패 시 예외 발생 + se.michaelthelin.spotify.model_objects.specification.Artist artist = api.getArtist(spotifyArtistId).build().execute(); // 메인 정보는 실패 시 예외 발생 Track[] topTracks = safeGetTopTracks(api, spotifyArtistId); Paging albums = safeGetAlbums(api, spotifyArtistId); @@ -210,8 +211,8 @@ public ArtistDetailResponse getArtistDetail( pickImageUrl(artist.getImages()), likeCount, albums != null ? albums.getTotal() : 0, - artist.getPopularity(), // (너가 별점으로 바꾸고 싶으면 여기 가공) - "", // Spotify에서는 설명을 제공하지 않아 공란 처리 + artist.getPopularity(), // 별점으로 수정 + "", // 설명 toAlbumResponses(albums != null ? albums.getItems() : null, spotifyArtistId), toTopTrackResponses(topTracks), relatedResponses @@ -252,14 +253,9 @@ private Paging safeGetAlbums(SpotifyApi api, String artistId) { } } - /** - * ✅ related artists - * - 정상 호출 - * - related가 비거나 404면 fallback(장르 기반 검색)으로 대체 - */ private List safeGetRelated( SpotifyApi api, - Artist me, + se.michaelthelin.spotify.model_objects.specification.Artist me, String artistGroup, long artistId, Long genreId @@ -271,7 +267,7 @@ private List safeGetRelated( } try { - Artist[] related = api.getArtistsRelatedArtists(id).build().execute(); + se.michaelthelin.spotify.model_objects.specification.Artist[] related = api.getArtistsRelatedArtists(id).build().execute(); if (related != null && related.length > 0) { log.info("Spotify related artists fetched: size={} spotifyArtistId={}", related.length, id); return toRelatedArtistResponses(related); @@ -365,7 +361,7 @@ private List toTopTrackResponses(Track[] tracks) { .collect(toList()); } - private List toRelatedArtistResponses(Artist[] artists) { + private List toRelatedArtistResponses(se.michaelthelin.spotify.model_objects.specification.Artist[] artists) { if (artists == null) return List.of(); return Stream.of(artists) .filter(Objects::nonNull)