Skip to content

Commit 6acbc72

Browse files
authored
feat: 트랙 추가, 삭제, 조회 구현 (#46)
* feat: 트랙 생성 구현 * refactor: 트랙 생성 시 상태코드 201로 변경 * feat: 트랙 삭제 구현 * feat: 위시리스트 내 트랙 조회 기능 구현
1 parent bd79fda commit 6acbc72

File tree

7 files changed

+215
-0
lines changed

7 files changed

+215
-0
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.amcamp.domain.track.api;
2+
3+
import com.amcamp.domain.track.application.TrackService;
4+
import com.amcamp.domain.track.dto.request.TrackCreateRequest;
5+
import com.amcamp.domain.track.dto.response.TrackInfoResponse;
6+
import lombok.RequiredArgsConstructor;
7+
import org.springframework.http.HttpStatus;
8+
import org.springframework.http.ResponseEntity;
9+
import org.springframework.web.bind.annotation.*;
10+
11+
import java.util.List;
12+
13+
@RestController
14+
@RequiredArgsConstructor
15+
@RequestMapping("/tracks")
16+
public class TrackController {
17+
18+
private final TrackService trackService;
19+
20+
@PostMapping
21+
public ResponseEntity<Void> trackCreate(@RequestBody List<TrackCreateRequest> request) {
22+
trackService.createTrack(request);
23+
return ResponseEntity.status(HttpStatus.CREATED).build();
24+
}
25+
26+
@DeleteMapping("/{trackId}")
27+
public ResponseEntity<Void> trackDelete(@PathVariable Long trackId) {
28+
trackService.deleteTrack(trackId);
29+
return ResponseEntity.ok().build();
30+
}
31+
32+
@GetMapping("/{wishlistId}")
33+
public List<TrackInfoResponse> trackFindAll(@PathVariable Long wishlistId) {
34+
return trackService.findAllTrack(wishlistId);
35+
}
36+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package com.amcamp.domain.track.application;
2+
3+
import com.amcamp.domain.member.domain.Member;
4+
import com.amcamp.domain.track.dao.TrackRepository;
5+
import com.amcamp.domain.track.domain.Track;
6+
import com.amcamp.domain.track.dto.request.TrackCreateRequest;
7+
import com.amcamp.domain.track.dto.response.TrackInfoResponse;
8+
import com.amcamp.domain.wishlist.dao.WishlistRepository;
9+
import com.amcamp.domain.wishlist.domain.Wishlist;
10+
import com.amcamp.global.error.exception.CustomException;
11+
import com.amcamp.global.error.exception.ErrorCode;
12+
import com.amcamp.global.util.MemberUtil;
13+
import lombok.RequiredArgsConstructor;
14+
import org.springframework.stereotype.Service;
15+
import org.springframework.transaction.annotation.Transactional;
16+
17+
import java.util.List;
18+
19+
@Transactional
20+
@Service
21+
@RequiredArgsConstructor
22+
public class TrackService {
23+
24+
private final MemberUtil memberUtil;
25+
private final WishlistRepository wishlistRepository;
26+
private final TrackRepository trackRepository;
27+
28+
public void createTrack(List<TrackCreateRequest> requests) {
29+
final Member currentMember = memberUtil.getCurrentMember();
30+
31+
List<Track> tracks = requests.stream()
32+
.map(request -> {
33+
Wishlist wishlist = findWishlistById(request.wishlistId());
34+
validateWishlistMemberMismatch(wishlist, currentMember);
35+
return Track.createTrack(
36+
request.artistName(), request.title(), request.albumName(), request.imageUrl(), wishlist);
37+
})
38+
.toList();
39+
40+
trackRepository.saveAll(tracks);
41+
}
42+
43+
public void deleteTrack(Long trackId) {
44+
final Member currentMember = memberUtil.getCurrentMember();
45+
final Track track = findTrackById(trackId);
46+
47+
validateTrackMemberMismatch(track, currentMember);
48+
49+
trackRepository.deleteById(trackId);
50+
}
51+
52+
@Transactional(readOnly = true)
53+
public List<TrackInfoResponse> findAllTrack(Long wishlistId) {
54+
final Member currentMember = memberUtil.getCurrentMember();
55+
final Wishlist wishlist = findWishlistById(wishlistId);
56+
57+
validateWishlistMemberMismatch(wishlist, currentMember);
58+
59+
return trackRepository.findAllByWishlistId(wishlist.getId())
60+
.stream()
61+
.map(TrackInfoResponse::from)
62+
.toList();
63+
}
64+
65+
private Wishlist findWishlistById(Long wishlistId) {
66+
return wishlistRepository.findById(wishlistId)
67+
.orElseThrow(() -> new CustomException(ErrorCode.WISHLIST_NOT_FOUND));
68+
}
69+
70+
private void validateWishlistMemberMismatch(Wishlist wishlist, Member member) {
71+
if (!wishlist.getMember().getId().equals(member.getId())) {
72+
throw new CustomException(ErrorCode.WISHLIST_MEMBER_MISMATCH);
73+
}
74+
}
75+
76+
private Track findTrackById(Long trackId) {
77+
return trackRepository.findById(trackId)
78+
.orElseThrow(() -> new CustomException(ErrorCode.TRACK_NOT_FOUND));
79+
}
80+
81+
private void validateTrackMemberMismatch(Track track, Member member) {
82+
if (!track.getWishlist().getMember().getId().equals(member.getId())) {
83+
throw new CustomException(ErrorCode.TRACK_MEMBER_MISMATCH);
84+
}
85+
}
86+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.amcamp.domain.track.dao;
2+
3+
import com.amcamp.domain.track.domain.Track;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
6+
import java.util.List;
7+
8+
public interface TrackRepository extends JpaRepository<Track, Long> {
9+
List<Track> findAllByWishlistId(Long wishlistId);
10+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.amcamp.domain.track.domain;
2+
3+
import com.amcamp.domain.common.model.BaseTimeEntity;
4+
import com.amcamp.domain.wishlist.domain.Wishlist;
5+
import jakarta.persistence.*;
6+
import lombok.AccessLevel;
7+
import lombok.Builder;
8+
import lombok.Getter;
9+
import lombok.NoArgsConstructor;
10+
11+
import static jakarta.persistence.FetchType.LAZY;
12+
13+
@Getter
14+
@Entity
15+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
16+
public class Track extends BaseTimeEntity {
17+
18+
@Id
19+
@GeneratedValue(strategy = GenerationType.IDENTITY)
20+
@Column(name = "track_id")
21+
private Long id;
22+
23+
private String artistName;
24+
25+
private String title;
26+
27+
private String albumName;
28+
29+
private String imageUrl;
30+
31+
@ManyToOne(fetch = LAZY)
32+
@JoinColumn(name = "wishlist_id")
33+
private Wishlist wishlist;
34+
35+
@Builder(access = AccessLevel.PRIVATE)
36+
public Track(String artistName, String title, String albumName, String imageUrl, Wishlist wishlist) {
37+
this.artistName = artistName;
38+
this.title = title;
39+
this.albumName = albumName;
40+
this.imageUrl = imageUrl;
41+
this.wishlist = wishlist;
42+
}
43+
44+
public static Track createTrack(
45+
String artistName, String title, String albumName, String imageUrl, Wishlist wishlist) {
46+
return Track.builder()
47+
.artistName(artistName)
48+
.title(title)
49+
.albumName(albumName)
50+
.imageUrl(imageUrl)
51+
.wishlist(wishlist)
52+
.build();
53+
}
54+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.amcamp.domain.track.dto.request;
2+
3+
public record TrackCreateRequest(
4+
Long wishlistId,
5+
String artistName,
6+
String title,
7+
String albumName,
8+
String imageUrl) {
9+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.amcamp.domain.track.dto.response;
2+
3+
import com.amcamp.domain.track.domain.Track;
4+
5+
public record TrackInfoResponse(
6+
Long trackId,
7+
String artistName,
8+
String title,
9+
String albumName,
10+
String imageUrl) {
11+
public static TrackInfoResponse from(Track track) {
12+
return new TrackInfoResponse(track.getId(), track.getArtistName(), track.getTitle(), track.getAlbumName(), track.getImageUrl());
13+
}
14+
}

src/main/java/com/amcamp/global/error/exception/ErrorCode.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ public enum ErrorCode {
1414
MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "회원을 찾을 수 없습니다."),
1515

1616
SPOTIFY_EXCEPTION(HttpStatus.SERVICE_UNAVAILABLE, "스포티파이 라이브러리 사용 중 예외가 발생했습니다."),
17+
18+
WISHLIST_NOT_FOUND(HttpStatus.NOT_FOUND, "위시리스트를 찾을 수 없습니다."),
19+
WISHLIST_MEMBER_MISMATCH(HttpStatus.FORBIDDEN, "위시리스트를 생성한 유저와 로그인된 계정이 일치하지 않습니다"),
20+
21+
TRACK_NOT_FOUND(HttpStatus.NOT_FOUND, "트랙을 찾을 수 없습니다."),
22+
TRACK_MEMBER_MISMATCH(HttpStatus.FORBIDDEN, "트랙을 생성한 유저와 로그인된 계정이 일치하지 않습니다."),
1723
;
1824

1925
private final HttpStatus httpStatus;

0 commit comments

Comments
 (0)