Skip to content

Commit

Permalink
Merge pull request #60 from Team-PLAT/feat/#44/playlist
Browse files Browse the repository at this point in the history
[FEAT] 플레이리스트 API
  • Loading branch information
kyxxgsoo authored Sep 26, 2024
2 parents 0c89886 + 3f27c9f commit 35de1ce
Show file tree
Hide file tree
Showing 17 changed files with 1,561 additions and 24 deletions.
1 change: 1 addition & 0 deletions src/main/java/com/cabin/plat/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.requestMatchers("/members/**").authenticated()
.requestMatchers("/tracks/**").authenticated()
.requestMatchers("/address/**").authenticated()
.requestMatchers("/playlists/**").authenticated()
.requestMatchers("images/**").authenticated()
.anyRequest().denyAll());

Expand Down
14 changes: 14 additions & 0 deletions src/main/java/com/cabin/plat/domain/member/entity/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.cabin.plat.global.common.BaseEntity;
import jakarta.persistence.*;
import java.time.LocalDateTime;
import java.util.Objects;
import lombok.*;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLRestriction;
Expand Down Expand Up @@ -52,4 +53,17 @@ public class Member extends BaseEntity {
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private SocialType socialType;

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Member)) return false;
Member member = (Member) o;
return id.equals(member.id) && clientId.equals(member.clientId);
}

@Override
public int hashCode() {
return Objects.hash(id, clientId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package com.cabin.plat.domain.playlist.controller;

import com.cabin.plat.config.AuthMember;
import com.cabin.plat.domain.member.entity.Member;
import com.cabin.plat.domain.playlist.dto.PlaylistRequest;
import com.cabin.plat.domain.playlist.dto.PlaylistResponse;
import com.cabin.plat.domain.playlist.service.PlaylistService;
import com.cabin.plat.global.common.BaseResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@RequestMapping("/playlists")
@Tag(name = "플레이리스트 API", description = "플레이리스트 관련 API 입니다.")
public class PlaylistController {

private final PlaylistService playlistService;

@Operation(summary = "플레이리스트 생성", description = "플레이리스트를 생성한다.")
@PostMapping
public BaseResponse<PlaylistResponse.PlayListId> addPlaylist(
@AuthMember Member member,
@RequestBody PlaylistRequest.PlaylistUpload playlistUpload) {

return BaseResponse.onSuccess(playlistService.addPlaylist(member, playlistUpload));
}

@Operation(summary = "플레이리스트 목록 조회", description = "사용자의 모든 플레이리스트를 가져온다. 페이지네이션을 지원합니다. page 파라미터에 페이지 번호를 입력해주세요.")
@GetMapping
public BaseResponse<PlaylistResponse.Playlists> getPlaylists(
@AuthMember Member member,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {

return BaseResponse.onSuccess(playlistService.getPlaylists(member, page, size));
}

@Operation(summary = "플레이리스트 목록 검색", description = "사용자의 플레이리스트를 제목으로 검색해서 가져온다. 페이지네이션을 지원합니다. page 파라미터에 페이지 번호를 입력해주세요.")
@GetMapping("/search")
public BaseResponse<PlaylistResponse.Playlists> getPlaylists(
@AuthMember Member member,
@RequestParam(defaultValue = "") String title,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {

return BaseResponse.onSuccess(playlistService.getSearchedPlaylists(member, title, page, size));
}

@Operation(summary = "플레이리스트 삭제", description = "플레이리스트를 삭제한다.")
@DeleteMapping("/{playlistId}")
public BaseResponse<PlaylistResponse.PlayListId> deletePlaylist(
@AuthMember Member member,
@PathVariable("playlistId") Long playlistId) {

return BaseResponse.onSuccess(playlistService.deletePlaylist(member, playlistId));
}

@Operation(summary = "플레이리스트 디테일 조회", description = "사용자의 플레이리스트 하나의 정보를 가져온다. 내부 트랙의 정보도 모두 가져온다.")
@GetMapping("/{playlistId}/detail")
public BaseResponse<PlaylistResponse.PlaylistDetail> getPlaylistDetail(
@AuthMember Member member,
@PathVariable("playlistId") Long playlistId) {

return BaseResponse.onSuccess(playlistService.getPlaylistDetail(member, playlistId));
}

@Operation(summary = "플레이리스트 정보 수정", description = "플레이리스트 사진과 제목을 편집한다.")
@PatchMapping("/{playlistId}")
public BaseResponse<PlaylistResponse.PlayListId> updatePlaylist(
@AuthMember Member member,
@PathVariable("playlistId") Long playlistId,
@RequestBody PlaylistRequest.PlaylistEdit playlistEdit) {

return BaseResponse.onSuccess(playlistService.updatePlaylistTitleAndImage(member, playlistId, playlistEdit));
}

@Operation(summary = "플레이리스트에 트랙 추가", description = "플레이리스트에 트랙을 추가한다.")
@PostMapping("/{playlistId}")
public BaseResponse<PlaylistResponse.PlayListId> addTrackToPlaylist(
@AuthMember Member member,
@PathVariable("playlistId") Long playlistId,
@RequestBody PlaylistRequest.TrackId trackId) {

return BaseResponse.onSuccess(playlistService.addTrackToPlaylist(member, playlistId, trackId));
}

@Operation(summary = "플레이리스트 트랙 순서 변경", description = "플레이리스트 내부의 트랙의 순서를 변경한다.")
@PatchMapping("/{playlistId}/tracks/order")
public BaseResponse<PlaylistResponse.PlayListId> updateTrackOrders(
@AuthMember Member member,
@PathVariable("playlistId") Long playlistId,
@RequestBody PlaylistRequest.PlaylistOrders playlistOrders) {

return BaseResponse.onSuccess(playlistService.updateTrackOrders(member, playlistId, playlistOrders));
}

@Operation(summary = "플레이리스트에서 트랙 삭제", description = "플레이리스트 내부의 트랙을 삭제합니다.")
@DeleteMapping("/{playlistId}/tracks/{trackId}")
public BaseResponse<PlaylistResponse.PlayListId> deleteTrackFromPlaylist(
@AuthMember Member member,
@PathVariable("playlistId") Long playlistId,
@PathVariable("trackId") Long trackId) {

return BaseResponse.onSuccess(playlistService.deleteTrackFromPlaylist(member, playlistId, trackId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.cabin.plat.domain.playlist.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import lombok.*;

public class PlaylistRequest {

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class PlaylistEdit {
@Schema(description = "플레이리스트 제목", example = "플레이리스트 제목")
private String title;

@Schema(description = "플레이리스트 이미지 URL", example = "https://s3.amazonaws.com/mybucket/images/sample.jpg")
private String playlistImageUrl;
}

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class PlaylistOrders {
private List<TrackOrder> tracks;
}

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class PlaylistUpload {
@Schema(description = "플레이리스트 제목", example = "플레이리스트 제목")
private String title;

@Schema(description = "플레이리스트 이미지 URL", example = "https://s3.amazonaws.com/mybucket/images/sample.jpg")
private String playlistImageUrl;

private List<TrackOrder> tracks;
}

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class TrackOrder {
@Schema(description = "트랙 고유 ID", example = "1")
private Long trackId;

@Schema(description = "플레이리스트에서 트랙의 순서", example = "0")
private int orderIndex;
}

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class TrackId {
@Schema(description = "트랙 고유 ID", example = "1")
private Long trackId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.cabin.plat.domain.playlist.dto;

import com.cabin.plat.domain.track.dto.TrackResponse.TrackDetail;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import lombok.*;

public class PlaylistResponse {
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class PlayListId {
@Schema(description = "플레이리스트 고유 ID", example = "1")
private Long playlistId;
}

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class Playlists {
private List<PlaylistInfo> playlists;

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class PlaylistInfo {
@Schema(description = "플레이리스트 고유 ID", example = "1")
private Long playlistId;

@Schema(description = "플레이리스트 제목", example = "플레이리스트 제목")
private String title;

@Schema(description = "플레이리스트 이미지 URL", example = "https://s3.amazonaws.com/mybucket/images/sample.jpg")
private String playlistImageUrl;

@Schema(description = "플레이리스트 생성일", example = "2024-09-22T07:23:09.102Z")
private LocalDateTime createdAt;

private Set<String> uploaderNicknames;
}
}

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class PlaylistDetail {
@Schema(description = "플레이리스트 고유 ID", example = "1")
private Long playlistId;

@Schema(description = "플레이리스트 제목", example = "플레이리스트 제목")
private String title;

@Schema(description = "플레이리스트 이미지 URL", example = "https://s3.amazonaws.com/mybucket/images/sample.jpg")
private String playlistImageUrl;

@Schema(description = "플레이리스트 생성일", example = "2024-09-22T07:23:09.102Z")
private LocalDateTime createdAt;

private List<TrackDetailOrder> tracks;
}

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class TrackDetailOrder {
@Schema(description = "트랙 순서", example = "1")
private int orderIndex;

@Schema(description = "트랙 상세 정보")
private TrackDetail trackDetail;
}
}
38 changes: 38 additions & 0 deletions src/main/java/com/cabin/plat/domain/playlist/entity/Playlist.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.cabin.plat.domain.playlist.entity;

import com.cabin.plat.domain.member.entity.Member;
import com.cabin.plat.global.common.BaseEntity;
import jakarta.persistence.*;
import java.util.Set;
import lombok.*;
import org.hibernate.annotations.SQLRestriction;

@Getter
@Builder
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@SQLRestriction("deleted_at is null")
public class Playlist extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "playlist_id")
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id", nullable = false)
private Member member;

@Column(nullable = false)
private String title;

private String playlistImageUrl;

@OneToMany(mappedBy = "playlist", cascade = CascadeType.ALL)
private Set<PlaylistTrack> playlistTracks;

public void updatePlaylist(String title, String playlistImageUrl) {
this.title = title;
this.playlistImageUrl = playlistImageUrl;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.cabin.plat.domain.playlist.entity;

import com.cabin.plat.domain.track.entity.Track;
import com.cabin.plat.global.common.BaseEntity;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.SQLRestriction;

@Getter
@Builder
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@SQLRestriction("deleted_at is null")
public class PlaylistTrack extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "track_playlist_id")
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "playlist_id", nullable = false)
private Playlist playlist;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "track_id", nullable = false)
private Track track;

@Setter
@Column(name = "order_index", nullable = false)
private int orderIndex;
}
Loading

0 comments on commit 35de1ce

Please sign in to comment.