diff --git a/src/main/java/org/sopt/lequuServer/domain/book/controller/BookController.java b/src/main/java/org/sopt/lequuServer/domain/book/controller/BookController.java index 1798e2d..d9dc97c 100644 --- a/src/main/java/org/sopt/lequuServer/domain/book/controller/BookController.java +++ b/src/main/java/org/sopt/lequuServer/domain/book/controller/BookController.java @@ -7,6 +7,7 @@ import lombok.RequiredArgsConstructor; import org.sopt.lequuServer.domain.book.dto.request.BookCreateRequestDto; import org.sopt.lequuServer.domain.book.dto.response.BookCreateResponseDto; +import org.sopt.lequuServer.domain.book.dto.response.BookDetailResponseDto; import org.sopt.lequuServer.domain.book.facade.BookFacade; import org.sopt.lequuServer.global.auth.jwt.JwtProvider; import org.sopt.lequuServer.global.common.dto.ApiResponse; @@ -38,4 +39,10 @@ public ApiResponse deleteBook(@PathVariable Long bookId) { bookFacade.deleteBook(bookId); return ApiResponse.success(SuccessType.BOOK_DELETE_SUCCESS); } + + @GetMapping("/detail/{bookUuid}") + @ResponseStatus(HttpStatus.OK) + public ApiResponse getBookDetail(@PathVariable String bookUuid) { + return ApiResponse.success(SuccessType.PROCESS_SUCCESS, bookFacade.getBookDetail(bookUuid)); + } } \ No newline at end of file diff --git a/src/main/java/org/sopt/lequuServer/domain/book/dto/response/BookDetailResponseDto.java b/src/main/java/org/sopt/lequuServer/domain/book/dto/response/BookDetailResponseDto.java new file mode 100644 index 0000000..ba89f2d --- /dev/null +++ b/src/main/java/org/sopt/lequuServer/domain/book/dto/response/BookDetailResponseDto.java @@ -0,0 +1,69 @@ +package org.sopt.lequuServer.domain.book.dto.response; + +import org.sopt.lequuServer.domain.book.model.Book; +import org.sopt.lequuServer.domain.note.dto.response.NoteDetailResponseDto; +import org.sopt.lequuServer.domain.note.model.Note; +import org.sopt.lequuServer.domain.sticker.dto.response.PostedStickerDetailResponseDto; +import org.sopt.lequuServer.domain.sticker.model.PostedSticker; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + +import static java.util.Comparator.*; + +public record BookDetailResponseDto( + Long bookId, + String favoriteImage, + String favoriteName, + String title, + String description, + String bookDate, + String bookNickname, + int bookBackgroundColor, + int noteNum, + List noteList, + List postedStickerList +) { + public static BookDetailResponseDto of(Book book) { + String bookDate = formatLocalDate(book); + + // 레큐노트 리스트를 noteId 기준 내림차순으로 정렬 + List sortedNotes = book.getNotes().stream() + .sorted(comparing(Note::getId).reversed()) + .toList(); + + // 레큐노트 리스트 가공 + int renderTypeCounter = 1; + List noteList = new ArrayList<>(); + for (Note note : sortedNotes) { + String background = note.getBackground(); + + if (background.endsWith(".jpg")) { + noteList.add(NoteDetailResponseDto.of(note, renderTypeCounter, -1, background)); + } else { + noteList.add(NoteDetailResponseDto.of(note, renderTypeCounter, Integer.parseInt(background), "")); + } + renderTypeCounter = (renderTypeCounter % 6 == 0) ? 1 : renderTypeCounter + 1; + } + + // 부착된 스티커 리스트 가공 + List postedStickers = book.getPostedStickers(); + List postedStickerList = new ArrayList<>(); + for (PostedSticker postedSticker : postedStickers) { + postedStickerList.add(PostedStickerDetailResponseDto.of(postedSticker)); + } + + return new BookDetailResponseDto(book.getId(), book.getFavoriteImage(), book.getFavoriteName(), + book.getTitle(), book.getDescription(), bookDate, book.getMember().getNickname(), + book.getBackgroundColor(), book.getNotes().size(), noteList, postedStickerList + ); + } + + private static String formatLocalDate(Book book) { + LocalDateTime createdAt = book.getCreatedAt(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd"); + return createdAt.format(formatter); + } +} \ No newline at end of file diff --git a/src/main/java/org/sopt/lequuServer/domain/book/facade/BookFacade.java b/src/main/java/org/sopt/lequuServer/domain/book/facade/BookFacade.java index 5df4710..673f893 100644 --- a/src/main/java/org/sopt/lequuServer/domain/book/facade/BookFacade.java +++ b/src/main/java/org/sopt/lequuServer/domain/book/facade/BookFacade.java @@ -3,6 +3,7 @@ import lombok.RequiredArgsConstructor; import org.sopt.lequuServer.domain.book.dto.request.BookCreateRequestDto; import org.sopt.lequuServer.domain.book.dto.response.BookCreateResponseDto; +import org.sopt.lequuServer.domain.book.dto.response.BookDetailResponseDto; import org.sopt.lequuServer.domain.book.model.Book; import org.sopt.lequuServer.domain.book.repository.BookRepository; import org.sopt.lequuServer.domain.book.service.BookService; @@ -28,11 +29,11 @@ public class BookFacade { private final BookService bookService; private final MemberRepository memberRepository; - private final S3Service s3Service; - private final NoteRepository noteRepository; - private final PostedStickerRepository postedStickerRepository; private final BookRepository bookRepository; + private final NoteRepository noteRepository; private final StickerRepository stickerRepository; + private final PostedStickerRepository postedStickerRepository; + private final S3Service s3Service; public BookCreateResponseDto createBook(BookCreateRequestDto request, Long memberId) { @@ -89,4 +90,10 @@ public void deleteBook(Long bookId) { // 정상적인 book id가 전송되면 bookRepository.deleteById(bookId); } + + public BookDetailResponseDto getBookDetail(String bookUuid) { + Book book = bookRepository.findByUuidOrThrow(bookUuid); + + return BookDetailResponseDto.of(book); + } } diff --git a/src/main/java/org/sopt/lequuServer/domain/book/repository/BookRepository.java b/src/main/java/org/sopt/lequuServer/domain/book/repository/BookRepository.java index 01fb6fe..aabed3d 100644 --- a/src/main/java/org/sopt/lequuServer/domain/book/repository/BookRepository.java +++ b/src/main/java/org/sopt/lequuServer/domain/book/repository/BookRepository.java @@ -8,6 +8,7 @@ import org.springframework.data.jpa.repository.Query; import java.util.List; +import java.util.Optional; public interface BookRepository extends JpaRepository { @@ -18,4 +19,11 @@ default Book findByIdOrThrow(Long id) { return this.findById(id).orElseThrow( () -> new CustomException(ErrorType.NOT_FOUND_BOOK_ERROR)); } + + Optional findByUuid(String uuid); + + default Book findByUuidOrThrow(String uuid) { + return this.findByUuid(uuid).orElseThrow( + () -> new CustomException(ErrorType.NOT_FOUND_BOOK_ERROR)); + } } \ No newline at end of file diff --git a/src/main/java/org/sopt/lequuServer/domain/note/dto/response/NoteCreateResponseDto.java b/src/main/java/org/sopt/lequuServer/domain/note/dto/response/NoteCreateResponseDto.java new file mode 100644 index 0000000..e7e9f23 --- /dev/null +++ b/src/main/java/org/sopt/lequuServer/domain/note/dto/response/NoteCreateResponseDto.java @@ -0,0 +1,12 @@ +package org.sopt.lequuServer.domain.note.dto.response; + +import org.sopt.lequuServer.domain.note.model.Note; + +public record NoteCreateResponseDto( + Long noteId, + String bookUuid +) { + public static NoteCreateResponseDto of(Note note) { + return new NoteCreateResponseDto(note.getId(), note.getBook().getUuid()); + } +} diff --git a/src/main/java/org/sopt/lequuServer/domain/note/dto/response/NoteDetailResponseDto.java b/src/main/java/org/sopt/lequuServer/domain/note/dto/response/NoteDetailResponseDto.java new file mode 100644 index 0000000..22b1efd --- /dev/null +++ b/src/main/java/org/sopt/lequuServer/domain/note/dto/response/NoteDetailResponseDto.java @@ -0,0 +1,29 @@ +package org.sopt.lequuServer.domain.note.dto.response; + +import org.sopt.lequuServer.domain.note.model.Note; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public record NoteDetailResponseDto( + Long noteId, + int renderType, + String content, + String noteDate, + String noteNickname, + int noteBackgroundColor, + String noteBackgroundImage +) { + public static NoteDetailResponseDto of(Note note, int renderType, int noteBackgroundColor, String noteBackgroundImage) { + String noteDate = formatLocalDate(note); + + return new NoteDetailResponseDto(note.getId(), renderType, note.getContent(), noteDate, + note.getMember().getNickname(), noteBackgroundColor, noteBackgroundImage); + } + + private static String formatLocalDate(Note note) { + LocalDateTime createdAt = note.getCreatedAt(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd"); + return createdAt.format(formatter); + } +} diff --git a/src/main/java/org/sopt/lequuServer/domain/note/dto/response/NoteResponseDto.java b/src/main/java/org/sopt/lequuServer/domain/note/dto/response/NoteResponseDto.java deleted file mode 100644 index e66ca99..0000000 --- a/src/main/java/org/sopt/lequuServer/domain/note/dto/response/NoteResponseDto.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.sopt.lequuServer.domain.note.dto.response; - -import org.sopt.lequuServer.domain.note.model.Note; - -public record NoteResponseDto( - Long noteId, - String bookUuid -) { - public static NoteResponseDto of(Note note) { - return new NoteResponseDto(note.getId(), note.getBook().getUuid()); - } -} diff --git a/src/main/java/org/sopt/lequuServer/domain/note/facade/NoteFacade.java b/src/main/java/org/sopt/lequuServer/domain/note/facade/NoteFacade.java index d298079..4a5ac96 100644 --- a/src/main/java/org/sopt/lequuServer/domain/note/facade/NoteFacade.java +++ b/src/main/java/org/sopt/lequuServer/domain/note/facade/NoteFacade.java @@ -6,7 +6,7 @@ import org.sopt.lequuServer.domain.member.model.Member; import org.sopt.lequuServer.domain.member.repository.MemberRepository; import org.sopt.lequuServer.domain.note.dto.request.NoteCreateDto; -import org.sopt.lequuServer.domain.note.dto.response.NoteResponseDto; +import org.sopt.lequuServer.domain.note.dto.response.NoteCreateResponseDto; import org.sopt.lequuServer.domain.note.model.Note; import org.sopt.lequuServer.domain.note.service.NoteService; import org.sopt.lequuServer.global.s3.service.S3Service; @@ -23,7 +23,7 @@ public class NoteFacade { private final NoteService noteService; private final S3Service s3Service; - public NoteResponseDto createNote(Long userId, NoteCreateDto noteCreateDto) { + public NoteCreateResponseDto createNote(Long userId, NoteCreateDto noteCreateDto) { Member member = memberRepository.findByIdOrThrow(userId); Book book = bookRepository.findByIdOrThrow(noteCreateDto.bookId()); diff --git a/src/main/java/org/sopt/lequuServer/domain/note/service/NoteService.java b/src/main/java/org/sopt/lequuServer/domain/note/service/NoteService.java index 29c0515..3e1ca24 100644 --- a/src/main/java/org/sopt/lequuServer/domain/note/service/NoteService.java +++ b/src/main/java/org/sopt/lequuServer/domain/note/service/NoteService.java @@ -3,7 +3,7 @@ import lombok.RequiredArgsConstructor; import org.sopt.lequuServer.domain.book.model.Book; import org.sopt.lequuServer.domain.member.model.Member; -import org.sopt.lequuServer.domain.note.dto.response.NoteResponseDto; +import org.sopt.lequuServer.domain.note.dto.response.NoteCreateResponseDto; import org.sopt.lequuServer.domain.note.model.Note; import org.sopt.lequuServer.domain.note.repository.NoteRepository; import org.springframework.stereotype.Service; @@ -17,11 +17,11 @@ public class NoteService { private final NoteRepository noteRepository; @Transactional - public NoteResponseDto saveNote(Note note, Member member, Book book) { + public NoteCreateResponseDto saveNote(Note note, Member member, Book book) { member.addNote(note); book.addNote(note); - - return NoteResponseDto.of(noteRepository.save(note)); + + return NoteCreateResponseDto.of(noteRepository.save(note)); } } diff --git a/src/main/java/org/sopt/lequuServer/domain/sticker/dto/response/PostedStickerDetailResponseDto.java b/src/main/java/org/sopt/lequuServer/domain/sticker/dto/response/PostedStickerDetailResponseDto.java new file mode 100644 index 0000000..81d0434 --- /dev/null +++ b/src/main/java/org/sopt/lequuServer/domain/sticker/dto/response/PostedStickerDetailResponseDto.java @@ -0,0 +1,15 @@ +package org.sopt.lequuServer.domain.sticker.dto.response; + +import org.sopt.lequuServer.domain.sticker.model.PostedSticker; + +public record PostedStickerDetailResponseDto( + Long postedStickerId, + String stickerImage, + int positionX, + int positionY +) { + public static PostedStickerDetailResponseDto of(PostedSticker postedSticker) { + return new PostedStickerDetailResponseDto(postedSticker.getId(), postedSticker.getSticker().getStickerImage(), + postedSticker.getPositionX(), postedSticker.getPositionY()); + } +} \ No newline at end of file diff --git a/src/main/java/org/sopt/lequuServer/global/common/model/BaseTimeEntity.java b/src/main/java/org/sopt/lequuServer/global/common/model/BaseTimeEntity.java index c3338e1..4728ef9 100644 --- a/src/main/java/org/sopt/lequuServer/global/common/model/BaseTimeEntity.java +++ b/src/main/java/org/sopt/lequuServer/global/common/model/BaseTimeEntity.java @@ -2,6 +2,7 @@ import jakarta.persistence.EntityListeners; import jakarta.persistence.MappedSuperclass; +import lombok.Getter; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; @@ -10,6 +11,7 @@ @MappedSuperclass @EntityListeners(AuditingEntityListener.class) +@Getter public abstract class BaseTimeEntity { @CreatedDate diff --git a/src/main/java/org/sopt/lequuServer/global/config/SecurityConfig.java b/src/main/java/org/sopt/lequuServer/global/config/SecurityConfig.java index 4d97bf1..be9ea9f 100644 --- a/src/main/java/org/sopt/lequuServer/global/config/SecurityConfig.java +++ b/src/main/java/org/sopt/lequuServer/global/config/SecurityConfig.java @@ -24,7 +24,7 @@ public class SecurityConfig { "/api/kakao/**", "/loading", "/error", "/api/login", "/api/reissue", "/api/test/**", "/health", "/actuator/health", "/api/images/**", "/", "/swagger-ui/**", "/swagger-resources/**", "/api-docs/**", - "/api/common/**" + "/api/common/**", "/api/books/detail/**" }; @Bean