Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package com.back.web7_9_codecrete_be.domain.artists.controller;

import com.back.web7_9_codecrete_be.domain.artists.dto.request.CreateRequest;
import com.back.web7_9_codecrete_be.domain.artists.dto.request.SearchRequest;
import com.back.web7_9_codecrete_be.domain.artists.dto.request.UpdateRequest;
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.dto.response.SearchResponse;
import com.back.web7_9_codecrete_be.domain.artists.service.ArtistService;
import com.back.web7_9_codecrete_be.domain.artists.service.ArtistEnrichService;
import com.back.web7_9_codecrete_be.global.rsData.RsData;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

Expand All @@ -22,7 +25,7 @@ public class ArtistsController {
private final ArtistService artistService;
private final ArtistEnrichService enrichService;

@Operation(summary = "아티스트 저장", description = "임의의 가수 300명(or 팀)을 DB에 저장합니다.")
@Operation(summary = "아티스트 저장", description = "임의의 가수(or 팀)을 DB에 저장합니다.")
@GetMapping("/saved")
public RsData<Integer> saveArtist() {
int saved = artistService.setArtist();
Expand All @@ -43,7 +46,7 @@ public RsData<Integer> enrich(
public RsData<Void> 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);
}

Expand Down Expand Up @@ -79,4 +82,54 @@ public RsData<Void> delete(
artistService.delete(id);
return RsData.success("아티스트 정보를 삭제했습니다.", null);
}

@Operation(summary = "아티스트 검색",
description = "아티스트 이름 또는 키워드를 입력하면, 해당 키워드가 포함된 아티스트 목록 또는 이름에 해당하는 아티스트를 조회합니다.")
@PostMapping("/search")
public RsData<List<SearchResponse>> search(
@Valid @RequestBody SearchRequest reqBody
) {
return RsData.success("아티스트 검색에 성공했습니다.", artistService.search(reqBody.artistName()));
}

@Operation(summary = "아티스트 찜하기", description = "id 에 해당하는 특정 아티스트를 찜합니다.")
@PostMapping("/likes/{id}")
public void artistLikes(
@PathVariable Long id
) {}

@Operation(summary = "아티스트 찜 해체", description = "id 에 해당하는 아티스트에게 등록했던 찜을 해제합니다.")
@DeleteMapping("/likes/{id}")
public void deleteArtistLikes(
@PathVariable Long id
) {}

@Operation(summary = "개인화된 공연 리스트 생성", description = "유저가 찜한 아티스트를 기반으로 공연 리스트를 생성합니다.")
@PostMapping("/list")
public void concertList() {}

@Operation(summary = "아티스트 인기 순위", description = "Spotify 인기도를 바탕으로 아티스트 인기 순위 랭킹을 제공합니다.")
@GetMapping("/ranking")
public void artistRanking() {}

@Operation(summary = "장르 기반 아티스트 추천", description = "찜한 장르를 기반으로 아티스트 추천 리스트를 제공합니다.")
@GetMapping("/recommendation/{genreId}")
public void recommendArtist(
@PathVariable Long genreId
) {}

@Operation(summary = "공연 셋리스트 생성", description = "사용자가 공연 셋리스트를 생성합니다.")
@PostMapping("/setlist/{concertId}/{artistId}")
public void makeSetlist(
@PathVariable Long concertId,
@PathVariable Long artistId
) {}

@Operation(summary = "공연 셋리스트 조회", description = "다른 사용자들이 생성한 셋리스트를 조회합니다.")
@GetMapping("/setlist/{concertId}/{artistId}")
public void getSetlist(
@PathVariable Long concertId,
@PathVariable Long artistId
) {}
}

Original file line number Diff line number Diff line change
@@ -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
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.back.web7_9_codecrete_be.domain.artists.dto.request;

import jakarta.validation.constraints.NotBlank;

public record SearchRequest(
@NotBlank(message = "검색어를 입력해주세요")
String artistName
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@notblank 외에도 다른 유효성 검증 수단을 추가하면 좋을 것 같습니다.
(정작 이 글 쓰는 팀장도 유효성 검증 수단을 빼먹은게... 👀

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 추가하겠습니다!!

) {
}
Original file line number Diff line number Diff line change
@@ -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
) {
}
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.back.web7_9_codecrete_be.domain.artists.dto.response;

import com.back.web7_9_codecrete_be.domain.artists.entity.Artist;

public record SearchResponse(
String artistName,
String artistGroup,
int likeCount
) {
public static SearchResponse from(Artist artist) {
return new SearchResponse(
artist.getArtistName(),
artist.getArtistGroup(),
artist.getLikeCount()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -36,22 +37,25 @@ public class Artist {
@Column(name = "name_ko", length = 200)
private String nameKo;

public Artist(String spotifyArtistId, String artistName, String artistGroup, String artistType, Genre genre) {
@Column(name = "like_count", nullable = false)
private int likeCount = 0;

public Artist(String spotifyArtistId, String artistName, String artistGroup, ArtistType artistType, Genre genre) {
this.spotifyArtistId = spotifyArtistId;
this.artistName = artistName;
this.artistGroup = artistGroup; // 옵션 B: seed에서는 null
this.artistType = artistType; // 옵션 B: seed에서는 "SINGER"
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"
Expand All @@ -65,7 +69,7 @@ public void changeGroup(String group) {
this.artistGroup = group;
}

public void changeType(String type) {
public void changeType(ArtistType type) {
this.artistType = type;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.back.web7_9_codecrete_be.domain.artists.entity;

public enum ArtistType {
SOLO,
GROUP
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository
public interface ArtistRepository extends JpaRepository<Artist, Long> {
Expand All @@ -21,4 +20,6 @@ public interface ArtistRepository extends JpaRepository<Artist, Long> {

List<Artist> findTop5ByArtistGroupAndIdNot(String artistGroup, long excludeId);
List<Artist> findTop5ByGenreIdAndIdNot(Long genreId, long excludeId);

List<Artist> findAllByArtistNameContainingIgnoreCaseOrNameKoContainingIgnoreCase(String artistName1, String artistName2);
}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import com.back.web7_9_codecrete_be.domain.artists.dto.request.UpdateRequest;
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.dto.response.SearchResponse;
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;
Expand All @@ -30,7 +32,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);
Expand Down Expand Up @@ -85,8 +87,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;
}

Expand All @@ -97,7 +99,7 @@ public void updateArtist(Long id, UpdateRequest req) {
}

if (!changed) {
throw new BusinessException(ArtistErrorCode.INVALID_UPDATE_REQUEST); // "수정할 값이 없습니다"
throw new BusinessException(ArtistErrorCode.INVALID_UPDATE_REQUEST);
}
}

Expand All @@ -108,4 +110,19 @@ public void delete(Long id) {
artistRepository.delete(artist);
}

@Transactional
public List<SearchResponse> search(String artistName) {
Comment on lines +113 to +114
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

조회이기 때문에 @transactional(readOnly = true)로 하면 더 좋을 것 같습니다!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넹 추가하겠습니다!!


List<Artist> artists =
artistRepository.findAllByArtistNameContainingIgnoreCaseOrNameKoContainingIgnoreCase(artistName, artistName);

if (artists.isEmpty()) {
throw new BusinessException(ArtistErrorCode.ARTIST_NOT_FOUND);
}

return artists.stream()
.map(SearchResponse::from)
.toList();
}

}
Loading