diff --git a/moun/src/main/java/io/moun/api/auction/domain/AuctionRepository.java b/moun/src/main/java/io/moun/api/auction/domain/AuctionRepository.java index e62a28b..9162456 100644 --- a/moun/src/main/java/io/moun/api/auction/domain/AuctionRepository.java +++ b/moun/src/main/java/io/moun/api/auction/domain/AuctionRepository.java @@ -2,5 +2,8 @@ import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface AuctionRepository extends JpaRepository<Auction, Long> { + List<Auction> findAuctionsByOrderByEndDateAsc(); } diff --git a/moun/src/main/java/io/moun/api/common/domain/SortType.java b/moun/src/main/java/io/moun/api/common/domain/SortType.java new file mode 100644 index 0000000..9773c96 --- /dev/null +++ b/moun/src/main/java/io/moun/api/common/domain/SortType.java @@ -0,0 +1,5 @@ +package io.moun.api.common.domain; + +public enum SortType { + EXPIRED_DATE, +} diff --git a/moun/src/main/java/io/moun/api/common/domain/StaticVariables.java b/moun/src/main/java/io/moun/api/common/domain/StaticVariables.java new file mode 100644 index 0000000..131883b --- /dev/null +++ b/moun/src/main/java/io/moun/api/common/domain/StaticVariables.java @@ -0,0 +1,5 @@ +package io.moun.api.common.domain; + +public class StaticVariables { + public static final String getURL = "GET /api/songs/{id}/"; +} diff --git a/moun/src/main/java/io/moun/api/common/service/MounFileService.java b/moun/src/main/java/io/moun/api/common/service/MounFileService.java index 8d0a9d4..65d0bf6 100644 --- a/moun/src/main/java/io/moun/api/common/service/MounFileService.java +++ b/moun/src/main/java/io/moun/api/common/service/MounFileService.java @@ -81,5 +81,8 @@ public MounFile getMounFileByFileName(String fileName) { return mounFileRepository.findMounFileByGeneratedFileName(fileName).orElseThrow( () -> new RuntimeException("Id not founded")); } - + + public void deleteById(Long fileId) { + mounFileRepository.deleteById(fileId); + } } diff --git a/moun/src/main/java/io/moun/api/song/controller/SongController.java b/moun/src/main/java/io/moun/api/song/controller/SongController.java index 5822808..51ae8a7 100644 --- a/moun/src/main/java/io/moun/api/song/controller/SongController.java +++ b/moun/src/main/java/io/moun/api/song/controller/SongController.java @@ -1,16 +1,19 @@ package io.moun.api.song.controller; import io.moun.api.song.controller.dto.SongAuctionVO; +import io.moun.api.song.controller.dto.SongRequest; import io.moun.api.song.controller.dto.SongResponse; import io.moun.api.song.domain.Song; import io.moun.api.song.service.SongService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @@ -47,9 +50,23 @@ public ResponseEntity<List<SongResponse>> getSongInformationByMember(@PathVariab return songService.findAllSongByMemberId(id); } - @GetMapping("/songs") //find all songs - read - public ResponseEntity<List<SongResponse>> getAllSongs() { - return songService.findAllSongs(); + @GetMapping("/songs") // find all songs / by genre & vibe / sortby - read + public ResponseEntity<List<SongResponse>> getAllSongs(@RequestParam(name = "genre", required = false) String genre, + @RequestParam(name = "vibe", required = false) String vibe, + @RequestParam(name = "sort", required = false) String sortType) { + + // 모든 필터가 없는 경우 + if (genre == null && vibe == null && sortType == null) { + return songService.findAllSongs(); + } + + // 정렬만 요청된 경우 + if (genre == null && vibe == null && !sortType.isEmpty()) { + return songService.findSongsBySortType(sortType); + } + + // 장르와 분위기 요청된 경우 (정렬 포함 가능) + return songService.findSongsByGenreAndVibe(genre, vibe); } @GetMapping("/songs/{id}/audio") //download song file - read @@ -61,4 +78,14 @@ public ResponseEntity<byte[]> downloadSongFileBySongId(@PathVariable Long id) th public ResponseEntity<byte[]> downloadImageFileBySongId(@PathVariable Long id) throws IOException { return songService.downloadImageFileBySongId(id); } + + @PatchMapping("/songs/{id}") + public ResponseEntity<String> updateSongEntity(@PathVariable Long id, @RequestBody SongRequest request) { + return songService.updateSong(id, request); + } + + @DeleteMapping("/songs/{id}") + public ResponseEntity<String> deleteSongById(@PathVariable Long id) { + return songService.deleteSongsById(id); + } } diff --git a/moun/src/main/java/io/moun/api/song/controller/dto/SongRequest.java b/moun/src/main/java/io/moun/api/song/controller/dto/SongRequest.java index 582683e..6d2aad6 100644 --- a/moun/src/main/java/io/moun/api/song/controller/dto/SongRequest.java +++ b/moun/src/main/java/io/moun/api/song/controller/dto/SongRequest.java @@ -1,7 +1,5 @@ package io.moun.api.song.controller.dto; -import io.moun.api.song.domain.GenreType; -import io.moun.api.song.domain.VibeType; import lombok.Data; import java.util.Set; @@ -11,6 +9,6 @@ public class SongRequest { private String title; private String description; - private Set<GenreType> songGenres; - private Set<VibeType> songVibes; + private Set<String> songGenres; + private Set<String> songVibes; } diff --git a/moun/src/main/java/io/moun/api/song/controller/dto/SongResponse.java b/moun/src/main/java/io/moun/api/song/controller/dto/SongResponse.java index 80781ca..a977582 100644 --- a/moun/src/main/java/io/moun/api/song/controller/dto/SongResponse.java +++ b/moun/src/main/java/io/moun/api/song/controller/dto/SongResponse.java @@ -2,7 +2,9 @@ import io.moun.api.auction.domain.Auction; import io.moun.api.common.BaseEntityResponse; +import io.moun.api.common.domain.StaticVariables; import io.moun.api.song.domain.GenreType; +import io.moun.api.song.domain.Song; import io.moun.api.song.domain.VibeType; import lombok.Builder; import lombok.Getter; @@ -45,5 +47,19 @@ public SongResponse(LocalDateTime createdDate, LocalDateTime lastModifiedDate, L this.auction = auction; } - + public static SongResponse create(Song song) { + return SongResponse.builder() + .id(song.getId()) + .title(song.getTitle()) + .description(song.getDescription()) + .songGenres(song.getSongGenres()) + .songVibes(song.getSongVibes()) + .songFileURL(StaticVariables.getURL + "audio") + .coverFileURL(StaticVariables.getURL + "image") + .createdDate(song.getCreatedDate()) + .lastModifiedDate(song.getLastModifiedDate()) + .memberId(song.getMember().getId()) + .auction(song.getAuction()) + .build(); + } } \ No newline at end of file diff --git a/moun/src/main/java/io/moun/api/song/domain/Song.java b/moun/src/main/java/io/moun/api/song/domain/Song.java index ae91219..81a1359 100644 --- a/moun/src/main/java/io/moun/api/song/domain/Song.java +++ b/moun/src/main/java/io/moun/api/song/domain/Song.java @@ -78,8 +78,15 @@ public Song(String title, String description, Set<GenreType> songGenres, Set<Vib this.auction = auction; } - public void update(MounFile songFile, MounFile coverImageFile) { + public void addFile(MounFile songFile, MounFile coverImageFile) { this.songFile = songFile; this.coverImageFile = coverImageFile; } + + public void update(String title, String description, Set<GenreType> genreTypes, Set<VibeType> vibeTypes) { + this.title = title; + this.description = description; + this.songGenres = genreTypes; + this.songVibes = vibeTypes; + } } diff --git a/moun/src/main/java/io/moun/api/song/domain/SongRepository.java b/moun/src/main/java/io/moun/api/song/domain/SongRepository.java index b26eb0e..4a1a610 100644 --- a/moun/src/main/java/io/moun/api/song/domain/SongRepository.java +++ b/moun/src/main/java/io/moun/api/song/domain/SongRepository.java @@ -11,5 +11,5 @@ public interface SongRepository extends JpaRepository<Song, Long> { List<Song> findAllByMember(Member member); - List<Song> findSongsByMember_Id(Long memberId); + List<Song> findSongsBySongGenresAndSongVibes(GenreType genre, VibeType vibe); } diff --git a/moun/src/main/java/io/moun/api/song/service/SongService.java b/moun/src/main/java/io/moun/api/song/service/SongService.java index ae15612..926957c 100644 --- a/moun/src/main/java/io/moun/api/song/service/SongService.java +++ b/moun/src/main/java/io/moun/api/song/service/SongService.java @@ -1,10 +1,10 @@ package io.moun.api.song.service; -import com.fasterxml.jackson.databind.ObjectMapper; import io.moun.api.auction.controller.dto.AuctionRequest; import io.moun.api.auction.domain.Auction; import io.moun.api.auction.domain.AuctionRepository; import io.moun.api.common.domain.MounFile; +import io.moun.api.common.domain.SortType; import io.moun.api.common.service.MounFileService; import io.moun.api.member.domain.Member; import io.moun.api.member.service.MemberQueryService; @@ -12,9 +12,12 @@ import io.moun.api.song.controller.dto.SongAuctionVO; import io.moun.api.song.controller.dto.SongRequest; import io.moun.api.song.controller.dto.SongResponse; +import io.moun.api.song.domain.GenreType; import io.moun.api.song.domain.Song; import io.moun.api.song.domain.SongRepository; +import io.moun.api.song.domain.VibeType; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -26,8 +29,13 @@ import java.io.IOException; import java.time.Duration; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +@Slf4j @Service @RequiredArgsConstructor @Transactional(readOnly = true) @@ -38,7 +46,6 @@ public class SongService { private final MemberQueryService memberQueryService; private final MounFileService mounFileService; private final JwtTokenHelper jwtTokenHelper; - private final ObjectMapper objectMapper; //music upload - file / api1 @Transactional @@ -49,7 +56,7 @@ public ResponseEntity<SongResponse> uploadSongRelatedFiles(Long id, MultipartFil Song song = songRepository.findById(id).orElseThrow(() -> new RuntimeException("Id not found")); - song.update(songUpload, coverUpload); + song.addFile(songUpload, coverUpload); SongResponse build = SongResponse.builder() .id(song.getId()) @@ -79,6 +86,30 @@ public ResponseEntity<Song> uploadSongAndAuction(SongAuctionVO songAuctionVO) { auctionRequest.setExpired(false); SongRequest songRequest = songAuctionVO.getSongRequest(); + Set<String> songGenres = songRequest.getSongGenres(); + Set<String> songVibes = songRequest.getSongVibes(); + + Set<GenreType> realGenres = new HashSet<>(); + Set<VibeType> realVibes = new HashSet<>(); + for (String songGenre : songGenres) { + GenreType genreType = GenreType.valueOf(songGenre.toUpperCase()); + + if (!songGenre.equalsIgnoreCase(genreType.toString())) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null); + } + + realGenres.add(genreType); + } + for (String songVibe: songVibes) { + VibeType vibeType = VibeType.valueOf(songVibe.toUpperCase()); + + if (!songVibe.equalsIgnoreCase(vibeType.toString())) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null); + } + + realVibes.add(vibeType); + } + //if start date is later than end date, also start bid is smaller than winning bid, return 400 / null if (Duration.between(auctionRequest.getStartDate().atStartOfDay(), auctionRequest.getEndDate().atStartOfDay()).toDays() <= 0 || auctionRequest.getStartBid() - auctionRequest.getWinningBid() > 0) { @@ -98,8 +129,8 @@ public ResponseEntity<Song> uploadSongAndAuction(SongAuctionVO songAuctionVO) { .auction(buildAuction) .title(songRequest.getTitle()) .description(songRequest.getDescription()) - .songGenres(songRequest.getSongGenres()) - .songVibes(songRequest.getSongVibes()) + .songGenres(realGenres) + .songVibes(realVibes) .build(); auctionRepository.save(buildAuction); @@ -130,7 +161,56 @@ public ResponseEntity<List<SongResponse>> findAllSongs() { return ResponseEntity.status(HttpStatus.OK).body(songResponseList); } - //get audio multifile + //find all songs by genre & vibe / api + public ResponseEntity<List<SongResponse>> findSongsByGenreAndVibe(String genreType, String vibeType) { + if (!genreType.isBlank() || !vibeType.isBlank()) { + + if (Arrays.stream(GenreType.values()).anyMatch(g -> g.name().equalsIgnoreCase(genreType)) && + Arrays.stream(VibeType.values()).anyMatch(v -> v.name().equalsIgnoreCase(vibeType))) { + + GenreType gt = GenreType.valueOf(genreType.toUpperCase()); + VibeType vt = VibeType.valueOf(vibeType.toUpperCase()); + + List<Song> songs = songRepository.findSongsBySongGenresAndSongVibes(gt, vt); + + List<SongResponse> songResponses = getSongResponses(songs); + + return ResponseEntity.status(HttpStatus.OK).body(songResponses); + } + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null); + } + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null); + } + + //find all songs by sort type / api + public ResponseEntity<List<SongResponse>> findSongsBySortType(String sortType) { + + if (!sortType.isBlank()) { + //later, add sort conditions here... + if (Arrays.stream(SortType.values()).noneMatch(s -> s.name().equalsIgnoreCase(sortType))) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null); + } + + List<Auction> auctions = auctionRepository.findAuctionsByOrderByEndDateAsc(); + + List<SongResponse> responses = new ArrayList<>(); + for (Auction auction : auctions) { + Song song = songRepository.findById(auction.getId()).orElseThrow(() -> new RuntimeException("Song id by Auction not found")); + + SongResponse build = SongResponse.create(song); + + responses.add(build); + } + + return ResponseEntity.status(HttpStatus.OK).body(responses); + } + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null); + } + + //download audio multifile / api public ResponseEntity<byte[]> downloadSongFileBySongId(Long id) throws IOException { Song song = songRepository.findById(id).orElseThrow(() -> new RuntimeException("Id not found")); @@ -148,7 +228,7 @@ public ResponseEntity<byte[]> downloadSongFileBySongId(Long id) throws IOExcepti .body(bytes); } - //get image multifile + //download image multifile / api public ResponseEntity<byte[]> downloadImageFileBySongId(Long id) throws IOException { Song song = songRepository.findById(id).orElseThrow(() -> new RuntimeException("Id not found")); @@ -166,31 +246,72 @@ public ResponseEntity<byte[]> downloadImageFileBySongId(Long id) throws IOExcept .body(bytes); } + //update song, auction entity / api + @Transactional + public ResponseEntity<String> updateSong(Long id, SongRequest updateRequest) { + Song song = songRepository.findById(id).orElseThrow(() -> new RuntimeException("Song id not found")); + + //genre & vibe + Set<String> updateGenres = updateRequest.getSongGenres(); + Set<GenreType> originGenres = song.getSongGenres(); + + if (!updateGenres.isEmpty()) { + originGenres = updateGenres.stream() + .map(s -> GenreType.valueOf(s.toUpperCase())) + .collect(Collectors.toSet()); + } + + Set<String> updateVibes = updateRequest.getSongVibes(); + Set<VibeType> originVibes = song.getSongVibes(); + + if (!updateVibes.isEmpty()) { + originVibes = updateVibes.stream() + .map(s -> VibeType.valueOf(s.toUpperCase())) + .collect(Collectors.toSet()); + } + + //title + String title = updateRequest.getTitle(); + if (title.isBlank()) { + title = song.getTitle(); + } + + //description + String description = updateRequest.getDescription(); + if (description.isBlank()) { + description = song.getDescription(); + } + + song.update(title, description, originGenres, originVibes); + + return ResponseEntity.status(HttpStatus.OK).body("Successful"); + } + + //delete song by id / api + @Transactional + public ResponseEntity<String> deleteSongsById(Long id) { + Song song = songRepository.findById(id).orElseThrow(() -> new RuntimeException("Song id not found")); + + + mounFileService.deleteById(song.getSongFile().getId()); + mounFileService.deleteById(song.getCoverImageFile().getId()); + auctionRepository.deleteById(id); + songRepository.deleteById(id); + + return ResponseEntity.status(HttpStatus.ACCEPTED).body("Delete successful"); + } //get songs list / method private List<SongResponse> getSongResponses(List<Song> songList) { - String getURL = "GET /api/songs/{id}/"; List<SongResponse> responses = new ArrayList<>(); for (Song song : songList) { String songName = song.getSongFile().getFileName(); String coverName = song.getCoverImageFile().getFileName(); - SongResponse build = SongResponse.builder() - .id(song.getId()) - .title(song.getTitle()) - .description(song.getDescription()) - .songGenres(song.getSongGenres()) - .songVibes(song.getSongVibes()) - .songFileURL(getURL + "audio") - .coverFileURL(getURL + "image") - .createdDate(song.getCreatedDate()) - .lastModifiedDate(song.getLastModifiedDate()) - .memberId(song.getMember().getId()) - .auction(song.getAuction()) - .build(); + SongResponse build = SongResponse.create(song); responses.add(build); }