Skip to content
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
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.domain.users.entity.User;
import com.back.web7_9_codecrete_be.global.rq.Rq;
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 @@ -21,8 +26,9 @@
public class ArtistsController {
private final ArtistService artistService;
private final ArtistEnrichService enrichService;
private final Rq rq;

@Operation(summary = "아티스트 저장", description = "임의의 가수 300명(or 팀)을 DB에 저장합니다.")
@Operation(summary = "아티스트 저장", description = "임의의 가수(or 팀)을 DB에 저장합니다.")
@GetMapping("/saved")
public RsData<Integer> saveArtist() {
int saved = artistService.setArtist();
Expand All @@ -41,9 +47,9 @@ public RsData<Integer> enrich(
@Operation(summary = "아티스트 생성", description = "아티스트를 등록합니다.")
@PostMapping()
public RsData<Void> create(
@RequestBody CreateRequest reqBody
@Valid @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 All @@ -65,7 +71,7 @@ public RsData<ArtistDetailResponse> artist(
@PatchMapping("/{id}")
public RsData<Void> update(
@PathVariable Long id,
@RequestBody UpdateRequest reqBody
@Valid @RequestBody UpdateRequest reqBody
) {
artistService.updateArtist(id, reqBody);
return RsData.success("아티스트 정보 수정을 완료했습니다.", null);
Expand All @@ -79,4 +85,62 @@ 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 RsData<Void> artistLikes(
@PathVariable Long id
) {
User user = rq.getUser();
artistService.likeArtist(id, user);
return RsData.success("아티스트 찜 성공", null);
}

@Operation(summary = "아티스트 찜 해체", description = "id 에 해당하는 아티스트에게 등록했던 찜을 해제합니다.")
@DeleteMapping("/likes/{id}")
public RsData<Void> deleteArtistLikes(
@PathVariable Long id
) {
User user = rq.getUser();
artistService.deleteLikeArtist(id, user);
return RsData.success("아티스트 찜 해제 성공", null);
}

@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,23 @@
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.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

public record CreateRequest(
@NotBlank(message = "아티스트 이름은 필수로 입력해야합니다.")
@Size(max = 200, message = "아티스트 이름은 200자를 넘길 수 없습니다.")
String artistName,

@Size(max = 150, message = "아티스트 그룹 이름은 150자를 넘길 수 없습니다.")
String artistGroup,
String artistType,

@NotNull(message = "아티스트 타입은 필수로 입력해야합니다(SOLO or GROUP)")
ArtistType artistType,

@NotBlank(message = "장르는 필수로 입력해야합니다.")
@Size(max = 30, message = "장르는 30자를 넘길 수 없습니다.")
String genreName
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.back.web7_9_codecrete_be.domain.artists.dto.request;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public record SearchRequest(
@NotBlank(message = "검색어를 입력해주세요")
@Size(max = 200, message = "아티스트 이름은 200자를 넘길 수 없습니다.")
String artistName
) {
}
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,13 +69,21 @@ public void changeGroup(String group) {
this.artistGroup = group;
}

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

public void changeGenre(Genre genre) {
this.genre = genre;
}

public void increaseLikeCount() {
this.likeCount++;
}

public void decreaseLikeCount() {
if (this.likeCount > 0) {
this.likeCount--;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,40 @@
package com.back.web7_9_codecrete_be.domain.artists.entity;

import com.back.web7_9_codecrete_be.domain.users.entity.User;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;


@Entity
@Getter
@NoArgsConstructor
@EntityListeners(AuditingEntityListener.class)
@Table(name = "artist_like")
public class ArtistLike {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "artist_like_id")
private long id;

@CreatedDate
@Column(name = "created_date", nullable = false)
private LocalDateTime createdDate;

// TODO : 추후 user Entity 보고 확인 예정 우선 주석 처리
/*
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "genre_id", nullable = false)
@JoinColumn(name = "user_id", nullable = false)
private User user;
*/

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "artist_id", nullable = false)
private Artist artist;

public ArtistLike(Artist artist, User user) {
this.artist = artist;
this.user = user;
}
}
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
@@ -1,10 +1,16 @@
package com.back.web7_9_codecrete_be.domain.artists.repository;

import com.back.web7_9_codecrete_be.domain.artists.entity.Artist;
import com.back.web7_9_codecrete_be.domain.artists.entity.ArtistLike;
import com.back.web7_9_codecrete_be.domain.users.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface ArtistLikeRepository extends JpaRepository<ArtistLike, Long> {
long countByArtistId(Long artistId);
boolean existsByArtistAndUser(Artist artist, User user);
Optional<ArtistLike> findByArtistAndUser(Artist artist, User user);
}
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
Loading