-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from JECT-Study/J01-2-be-북마크-작업
J01 2 be 북마크 작업
- Loading branch information
Showing
19 changed files
with
639 additions
and
7 deletions.
There are no files selected for viewing
46 changes: 46 additions & 0 deletions
46
src/main/java/ject/componote/domain/bookmark/api/BookmarkController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package ject.componote.domain.bookmark.api; | ||
|
||
import jakarta.validation.Valid; | ||
import ject.componote.domain.auth.model.AuthPrincipal; | ||
import ject.componote.domain.auth.model.Authenticated; | ||
import ject.componote.domain.bookmark.application.BookmarkService; | ||
import ject.componote.domain.bookmark.dto.request.BookmarkRequest; | ||
import ject.componote.domain.bookmark.dto.response.BookmarkResponse; | ||
import ject.componote.domain.common.dto.response.PageResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.data.web.PageableDefault; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.*; | ||
|
||
@RequestMapping("/bookmarks") | ||
@RequiredArgsConstructor | ||
@RestController | ||
public class BookmarkController { | ||
|
||
private final BookmarkService bookmarkService; | ||
|
||
@PostMapping | ||
public ResponseEntity<BookmarkResponse> addBookmark( | ||
@Authenticated AuthPrincipal authPrincipal, | ||
@Valid @RequestBody BookmarkRequest bookmarkRequest) { | ||
return ResponseEntity.ok(bookmarkService.addBookmark(authPrincipal, bookmarkRequest)); | ||
} | ||
|
||
@GetMapping | ||
public ResponseEntity<PageResponse<BookmarkResponse>> getBookmark( | ||
@Authenticated final AuthPrincipal authPrincipal, | ||
@PageableDefault(size = 10, page = 0) Pageable pageable, | ||
@RequestParam(required = false, defaultValue = "component") String type, | ||
@RequestParam(required = false, defaultValue = "createdAt") String sortType) { | ||
|
||
return ResponseEntity.ok( | ||
bookmarkService.getBookmark(authPrincipal, pageable, type, sortType)); | ||
} | ||
|
||
@DeleteMapping | ||
public ResponseEntity<BookmarkResponse> deleteBookmark(@Authenticated final AuthPrincipal authPrincipal, @Valid @RequestBody BookmarkRequest bookmarkRequest) { | ||
return ResponseEntity.ok() | ||
.body(bookmarkService.deleteBookmark(authPrincipal, bookmarkRequest)); | ||
} | ||
} |
164 changes: 164 additions & 0 deletions
164
src/main/java/ject/componote/domain/bookmark/application/BookmarkService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
package ject.componote.domain.bookmark.application; | ||
|
||
import ject.componote.domain.auth.dao.MemberRepository; | ||
import ject.componote.domain.auth.domain.Member; | ||
import ject.componote.domain.auth.model.AuthPrincipal; | ||
import ject.componote.domain.bookmark.domain.Bookmark; | ||
import ject.componote.domain.bookmark.domain.BookmarkRepository; | ||
import ject.componote.domain.bookmark.dto.request.BookmarkRequest; | ||
import ject.componote.domain.bookmark.dto.response.BookmarkResponse; | ||
import ject.componote.domain.bookmark.error.ExistedBookmarkError; | ||
import ject.componote.domain.bookmark.error.InvalidBookmarkTypeError; | ||
import ject.componote.domain.bookmark.error.NotFoundBookmarkException; | ||
import ject.componote.domain.bookmark.error.NotFoundComponentException; | ||
import ject.componote.domain.bookmark.error.NotFoundDesignSystemException; | ||
import ject.componote.domain.bookmark.error.NotFoundMemberException; | ||
import ject.componote.domain.common.dto.response.PageResponse; | ||
import ject.componote.domain.component.domain.Component; | ||
import ject.componote.domain.component.domain.ComponentRepository; | ||
import ject.componote.domain.design.domain.Design; | ||
import ject.componote.domain.design.domain.DesignSystem; | ||
import ject.componote.domain.design.domain.DesignSystemRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.data.domain.Page; | ||
import org.springframework.data.domain.PageRequest; | ||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.data.domain.Sort; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
@RequiredArgsConstructor | ||
@Service | ||
public class BookmarkService { | ||
|
||
private final BookmarkRepository bookmarkRepository; | ||
private final MemberRepository memberRepository; | ||
private final ComponentRepository componentRepository; | ||
private final DesignSystemRepository designSystemRepository; | ||
|
||
@Transactional | ||
public BookmarkResponse addBookmark(AuthPrincipal authPrincipal, BookmarkRequest bookmarkRequest) { | ||
// 북마크 중복 여부 확인 | ||
if (bookmarkRepository.existsByMemberIdAndResourceIdAndType( | ||
authPrincipal.id(), bookmarkRequest.id(), bookmarkRequest.type())) { | ||
throw new ExistedBookmarkError( | ||
authPrincipal.id(), bookmarkRequest.id(), bookmarkRequest.type()); | ||
} | ||
|
||
Member member = findMemberOrThrow(authPrincipal.id()); | ||
Bookmark bookmark; | ||
|
||
if ("component".equals(bookmarkRequest.type())) { | ||
Component component = findComponentOrThrow(bookmarkRequest.id()); | ||
bookmark = Bookmark.of(member, component); | ||
bookmarkRepository.save(bookmark); | ||
return BookmarkResponse.from(bookmark, component); | ||
} else if ("designSystem".equals(bookmarkRequest.type())) { | ||
Design designSystem = findDesignSystemOrThrow(bookmarkRequest.id()); | ||
bookmark = Bookmark.of(member, designSystem); | ||
bookmarkRepository.save(bookmark); | ||
return BookmarkResponse.from(bookmark, designSystem); | ||
} else { | ||
throw new InvalidBookmarkTypeError(bookmarkRequest.type()); | ||
} | ||
} | ||
|
||
@Transactional(readOnly = true) | ||
public PageResponse<BookmarkResponse> getBookmark( | ||
AuthPrincipal authPrincipal, Pageable pageable, String type, String sortType) { | ||
|
||
Page<Bookmark> bookmarks; | ||
|
||
Pageable sortedPageable = applySort(pageable, type, sortType); | ||
|
||
if ("component".equals(type)) { | ||
bookmarks = bookmarkRepository.findAllByMemberIdAndType( | ||
authPrincipal.id(), "component", sortedPageable); | ||
} else if ("designSystem".equals(type)) { | ||
bookmarks = bookmarkRepository.findAllByMemberIdAndType( | ||
authPrincipal.id(), "designSystem", sortedPageable); | ||
} else { | ||
throw new InvalidBookmarkTypeError(type); | ||
} | ||
|
||
Page<BookmarkResponse> bookmarkResponsePage = bookmarks.map(bookmark -> { | ||
if ("component".equals(bookmark.getType())) { | ||
Component component = findComponentOrThrow(bookmark.getResourceId()); | ||
return BookmarkResponse.from(bookmark, component); | ||
} else { | ||
Design designSystem = findDesignSystemOrThrow(bookmark.getResourceId()); | ||
return BookmarkResponse.from(bookmark, designSystem); | ||
} | ||
}); | ||
|
||
return PageResponse.from(bookmarkResponsePage); | ||
} | ||
|
||
@Transactional | ||
public BookmarkResponse deleteBookmark(AuthPrincipal authPrincipal, BookmarkRequest bookmarkRequest) { | ||
Bookmark bookmark = bookmarkRepository.findByMemberIdAndResourceIdAndType( | ||
authPrincipal.id(), bookmarkRequest.id(), bookmarkRequest.type()) | ||
.orElseThrow(() -> new NotFoundBookmarkException( | ||
authPrincipal.id(), bookmarkRequest.id(), bookmarkRequest.type())); | ||
|
||
bookmarkRepository.delete(bookmark); | ||
|
||
if ("component".equals(bookmarkRequest.type())) { | ||
Component component = findComponentOrThrow(bookmarkRequest.id()); | ||
return BookmarkResponse.from(bookmark, component); | ||
} else if ("designSystem".equals(bookmarkRequest.type())) { | ||
Design designSystem = findDesignSystemOrThrow(bookmarkRequest.id()); | ||
return BookmarkResponse.from(bookmark, designSystem); | ||
} else { | ||
throw new InvalidBookmarkTypeError(bookmarkRequest.type()); | ||
} | ||
} | ||
|
||
private Member findMemberOrThrow(Long memberId) { | ||
return memberRepository.findById(memberId) | ||
.orElseThrow(() -> new NotFoundMemberException(memberId)); | ||
} | ||
|
||
private Component findComponentOrThrow(Long componentId) { | ||
return componentRepository.findById(componentId) | ||
.orElseThrow(() -> new NotFoundComponentException(componentId)); | ||
} | ||
|
||
private Design findDesignSystemOrThrow(Long designSystemId) { | ||
return designSystemRepository.findById(designSystemId) | ||
.orElseThrow(() -> new NotFoundDesignSystemException(designSystemId)); | ||
} | ||
|
||
private Pageable applySort(Pageable pageable, String type, String sortType) { | ||
Sort sort; | ||
|
||
if ("createdAt".equals(sortType)) { | ||
sort = Sort.by(Sort.Order.desc("createdAt")); | ||
} | ||
else if ("name".equals(sortType) && "component".equals(type)) { | ||
sort = Sort.by(Sort.Order.asc("summary.title")) | ||
.and(Sort.by(Sort.Order.desc("createdAt"))); | ||
} else if ("viewCount".equals(sortType) && "component".equals(type)) { | ||
sort = Sort.by(Sort.Order.desc("summary.viewCount")) | ||
.and(Sort.by(Sort.Order.desc("createdAt"))); | ||
} else if ("commentCount".equals(sortType) && "component".equals(type)) { | ||
sort = Sort.by(Sort.Order.desc("commentCount")) | ||
.and(Sort.by(Sort.Order.desc("createdAt"))); | ||
} | ||
|
||
else if ("name".equals(sortType) && "designSystem".equals(type)) { | ||
sort = Sort.by(Sort.Order.asc("summary.name")) | ||
.and(Sort.by(Sort.Order.desc("createdAt"))); | ||
} else if ("viewCount".equals(sortType) && "designSystem".equals(type)) { | ||
sort = Sort.by(Sort.Order.desc("summary.viewCount")) | ||
.and(Sort.by(Sort.Order.desc("createdAt"))); | ||
} else if ("recommendCount".equals(sortType) && "designSystem".equals(type)) { | ||
sort = Sort.by(Sort.Order.desc("summary.recommendCount")) | ||
.and(Sort.by(Sort.Order.desc("createdAt"))); | ||
} else { | ||
sort = Sort.by(Sort.Order.desc("createdAt")); | ||
} | ||
|
||
return PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), sort); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
src/main/java/ject/componote/domain/bookmark/domain/BookmarkRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package ject.componote.domain.bookmark.domain; | ||
|
||
import org.springframework.data.domain.Page; | ||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
import java.util.Optional; | ||
import org.springframework.stereotype.Repository; | ||
|
||
@Repository | ||
public interface BookmarkRepository extends JpaRepository<Bookmark, Long> { | ||
boolean existsByMemberIdAndResourceIdAndType(Long memberId, Long resourceId, String type); | ||
|
||
Optional<Bookmark> findByMemberIdAndResourceIdAndType(Long memberId, Long resourceId, String type); | ||
|
||
Page<Bookmark> findAllByMemberIdAndType(Long memberId, String type, Pageable pageable); | ||
} |
8 changes: 8 additions & 0 deletions
8
src/main/java/ject/componote/domain/bookmark/dto/request/BookmarkRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package ject.componote.domain.bookmark.dto.request; | ||
|
||
import com.google.firebase.database.annotations.NotNull; | ||
|
||
public record BookmarkRequest( | ||
@NotNull Long id, | ||
@NotNull String type // "component" or "designSystem" | ||
) {} |
51 changes: 51 additions & 0 deletions
51
src/main/java/ject/componote/domain/bookmark/dto/response/BookmarkResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package ject.componote.domain.bookmark.dto.response; | ||
|
||
import ject.componote.domain.bookmark.domain.Bookmark; | ||
import ject.componote.domain.common.model.BaseImage; | ||
import ject.componote.domain.component.domain.Component; | ||
import ject.componote.domain.design.domain.Design; | ||
import ject.componote.domain.design.domain.DesignSystem; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
public record BookmarkResponse( | ||
Long bookmarkId, | ||
String type, | ||
Long resourceId, | ||
String resourceName, | ||
String organization, | ||
BaseImage thumbnailUrl, | ||
Long bookmarkCount, | ||
Long commentCount, | ||
LocalDateTime createdAt | ||
) { | ||
// Component 북마크 변환 | ||
public static BookmarkResponse from(Bookmark bookmark, Component component) { | ||
return new BookmarkResponse( | ||
bookmark.getId(), | ||
"component", | ||
component.getId(), | ||
component.getSummary().getTitle(), | ||
null, | ||
component.getSummary().getThumbnail(), | ||
component.getBookmarkCount().getValue(), | ||
component.getCommentCount().getValue(), | ||
bookmark.getCreatedAt() | ||
); | ||
} | ||
|
||
// DesignSystem 북마크 변환 | ||
public static BookmarkResponse from(Bookmark bookmark, Design designSystem) { | ||
return new BookmarkResponse( | ||
bookmark.getId(), | ||
"designSystem", | ||
designSystem.getId(), | ||
designSystem.getSummary().getName(), | ||
designSystem.getSummary().getOrganization(), | ||
designSystem.getSummary().getThumbnail(), | ||
designSystem.getBookmarkCount().getValue(), | ||
null, | ||
bookmark.getCreatedAt() | ||
); | ||
} | ||
} |
8 changes: 7 additions & 1 deletion
8
src/main/java/ject/componote/domain/bookmark/error/BookmarkException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,10 @@ | ||
package ject.componote.domain.bookmark.error; | ||
|
||
public class BookmarkException { | ||
import ject.componote.global.error.ComponoteException; | ||
import org.springframework.http.HttpStatus; | ||
|
||
public class BookmarkException extends ComponoteException { | ||
public BookmarkException(final String message, final HttpStatus status) { | ||
super(message, status); | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
src/main/java/ject/componote/domain/bookmark/error/ExistedBookmarkError.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package ject.componote.domain.bookmark.error; | ||
|
||
import org.springframework.http.HttpStatus; | ||
|
||
public class ExistedBookmarkError extends BookmarkException { | ||
public ExistedBookmarkError(final Long memberId, final Long resourceId, final String type) { | ||
super(String.format("해당 회원은 이미 %s를 북마크에 추가하였습니다. 소셜 ID : %d, %s ID : %d", type, memberId, type, resourceId), HttpStatus.BAD_REQUEST); | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
src/main/java/ject/componote/domain/bookmark/error/ExistedComponentError.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package ject.componote.domain.bookmark.error; | ||
|
||
import org.springframework.http.HttpStatus; | ||
|
||
public class ExistedComponentError extends BookmarkException { | ||
public ExistedComponentError(final Long memberId, final Long componentId) { | ||
super(String.format("해당 회원은 이미 해당 컴포넌트를 북마크에 추가하였습니다. 소셜 ID : %d, 컴포넌트 ID : %d", memberId, componentId), HttpStatus.BAD_REQUEST); | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
src/main/java/ject/componote/domain/bookmark/error/InvalidBookmarkTypeError.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package ject.componote.domain.bookmark.error; | ||
|
||
import org.springframework.http.HttpStatus; | ||
|
||
public class InvalidBookmarkTypeError extends BookmarkException { | ||
public InvalidBookmarkTypeError(final String type) { | ||
super(String.format("유효하지 않은 북마크 타입입니다: %s", type), HttpStatus.BAD_REQUEST); | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
src/main/java/ject/componote/domain/bookmark/error/NotFoundBookmarkException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package ject.componote.domain.bookmark.error; | ||
|
||
import org.springframework.http.HttpStatus; | ||
|
||
public class NotFoundBookmarkException extends BookmarkException { | ||
public NotFoundBookmarkException(final Long memberId, final Long resourceId, final String type) { | ||
super(String.format("해당 회원의 일치하는 북마크를 찾을 수 없습니다. 회원 ID : %d, %s ID : %d", memberId, type, resourceId), HttpStatus.NOT_FOUND); | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
src/main/java/ject/componote/domain/bookmark/error/NotFoundComponentException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package ject.componote.domain.bookmark.error; | ||
|
||
import org.springframework.http.HttpStatus; | ||
|
||
public class NotFoundComponentException extends BookmarkException { | ||
public NotFoundComponentException(final Long componentId) { | ||
super(String.format("일치하는 컴포넌트를 찾을 수 없습니다. 컴포넌트 ID : %d", componentId), HttpStatus.NOT_FOUND); | ||
} | ||
} |
Oops, something went wrong.