diff --git a/src/main/java/com/sesac/boheommong/domain/bookmark/controller/BookmarkController.java b/src/main/java/com/sesac/boheommong/domain/bookmark/controller/BookmarkController.java index ce890fb..b040b5f 100644 --- a/src/main/java/com/sesac/boheommong/domain/bookmark/controller/BookmarkController.java +++ b/src/main/java/com/sesac/boheommong/domain/bookmark/controller/BookmarkController.java @@ -1,51 +1,102 @@ package com.sesac.boheommong.domain.bookmark.controller; -import com.sesac.boheommong.domain.bookmark.dto.request.BookmarkRequestDto; import com.sesac.boheommong.domain.bookmark.dto.response.BookmarkResponseDto; import com.sesac.boheommong.domain.bookmark.service.BookmarkService; +import com.sesac.boheommong.domain.bookmark.swagger.*; +import com.sesac.boheommong.global.jwt.service.TokenProvider; import com.sesac.boheommong.global.response.Response; +import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController -@RequestMapping("/api") +@RequestMapping("/api/bookmarks") @RequiredArgsConstructor public class BookmarkController { private final BookmarkService bookmarkService; + private final TokenProvider tokenProvider; - @GetMapping("/bookmarks/state") + /** + * 특정 상품이 북마크 되었는지 여부 조회 + */ + @GetBookmarkState + @GetMapping("/state") public Response getBookmarkState( - @RequestParam String loginEmail, + HttpServletRequest request, @RequestParam Long productId ) { + String loginEmail = tokenProvider.getUserLoginEmail(request); Boolean state = bookmarkService.getState(loginEmail, productId); return Response.success(state); } - @GetMapping("/bookmarks/user/{userId}") - public Response> getAllBookmarksByUser(@PathVariable Long userId) { - List results = bookmarkService.getAllBookmarksByUser(userId); + /** + * 로그인 유저의 모든 북마크 조회 + */ + @GetAllBookmarks + @GetMapping + public Response> getAllBookmarks(HttpServletRequest request) { + String loginEmail = tokenProvider.getUserLoginEmail(request); + List results = bookmarkService.getAllBookmarksByLoginEmail(loginEmail); return Response.success(results); } - @PostMapping("/bookmarks") - public Response createBookmark(@RequestBody BookmarkRequestDto requestDto) { - BookmarkResponseDto created = bookmarkService.createBookmark(requestDto); + /** + * 북마크 추가/취소(Toggle) + */ + @PostToggleBookmark + @PostMapping("/products/{productId}/toggle") + public Response addOrCancelBookmark( + HttpServletRequest request, + @PathVariable("productId") Long productId + ) { + String loginEmail = tokenProvider.getUserLoginEmail(request); + bookmarkService.addOrCancelBookmark(loginEmail, productId); + return Response.success(); + } + + /** + * 북마크 생성 + */ + @PostCreateBookmark + @PostMapping + public Response createBookmark( + HttpServletRequest request, + @RequestParam Long productId + ) { + String loginEmail = tokenProvider.getUserLoginEmail(request); + BookmarkResponseDto created = bookmarkService.createBookmark(loginEmail, productId); return Response.success(created); } - @GetMapping("/bookmarks/{bookmarkId}") - public Response getBookmark(@PathVariable Long bookmarkId) { - BookmarkResponseDto result = bookmarkService.getBookmark(bookmarkId); + /** + * 특정 북마크 단건 조회 + */ + @GetBookmarkById + @GetMapping("/{bookmarkId}") + public Response getBookmark( + HttpServletRequest request, + @PathVariable Long bookmarkId + ) { + String loginEmail = tokenProvider.getUserLoginEmail(request); + BookmarkResponseDto result = bookmarkService.getBookmark(loginEmail, bookmarkId); return Response.success(result); } - @DeleteMapping("/bookmarks/{bookmarkId}") - public Response deleteBookmark(@PathVariable Long bookmarkId) { - bookmarkService.deleteBookmark(bookmarkId); + /** + * 북마크 삭제(소프트 삭제) + */ + @DeleteBookmark + @DeleteMapping("/{bookmarkId}") + public Response deleteBookmark( + HttpServletRequest request, + @PathVariable Long bookmarkId + ) { + String loginEmail = tokenProvider.getUserLoginEmail(request); + bookmarkService.deleteBookmark(loginEmail, bookmarkId); return Response.success(); } } diff --git a/src/main/java/com/sesac/boheommong/domain/bookmark/dto/request/BookmarkRequestDto.java b/src/main/java/com/sesac/boheommong/domain/bookmark/dto/request/BookmarkRequestDto.java deleted file mode 100644 index c7fcf95..0000000 --- a/src/main/java/com/sesac/boheommong/domain/bookmark/dto/request/BookmarkRequestDto.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.sesac.boheommong.domain.bookmark.dto.request; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@NoArgsConstructor // 직렬화/역직렬화를 위해 기본 생성자 -@AllArgsConstructor // 모든 필드를 받는 생성자 -public class BookmarkRequestDto { - private Long userId; // 실제로는 jwt에서 userId를 꺼낼 수도 있지만, 여기서는 직접 전달받는다고 가정 - private Long productId; // 북마크할 상품 ID -} diff --git a/src/main/java/com/sesac/boheommong/domain/bookmark/entity/Bookmark.java b/src/main/java/com/sesac/boheommong/domain/bookmark/entity/Bookmark.java index d00b7b1..de372dc 100644 --- a/src/main/java/com/sesac/boheommong/domain/bookmark/entity/Bookmark.java +++ b/src/main/java/com/sesac/boheommong/domain/bookmark/entity/Bookmark.java @@ -7,15 +7,12 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import org.springframework.data.jpa.domain.support.AuditingEntityListener; @Entity @Getter -@Table(name = "bookmarks", uniqueConstraints = { - @UniqueConstraint(columnNames = {"user_id", "product_id"}) -}) -@EntityListeners(AuditingEntityListener.class) @NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(name = "bookmarks", + uniqueConstraints = @UniqueConstraint(columnNames = {"user_id", "product_id"})) public class Bookmark extends BaseEntity { @Id diff --git a/src/main/java/com/sesac/boheommong/domain/bookmark/repository/BookmarkRepository.java b/src/main/java/com/sesac/boheommong/domain/bookmark/repository/BookmarkRepository.java index af2a905..20d6844 100644 --- a/src/main/java/com/sesac/boheommong/domain/bookmark/repository/BookmarkRepository.java +++ b/src/main/java/com/sesac/boheommong/domain/bookmark/repository/BookmarkRepository.java @@ -4,6 +4,8 @@ import com.sesac.boheommong.domain.insurance.entity.InsuranceProduct; import com.sesac.boheommong.domain.user.entity.User; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.util.List; @@ -15,4 +17,9 @@ public interface BookmarkRepository extends JpaRepository { Optional findByUserAndProduct(User user, InsuranceProduct product); List findAllByUserOrderByCreatedAtDesc(User user); + + @Query("SELECT b FROM Bookmark b " + + "WHERE b.user.userId = :userId AND b.product.productId = :productId") + Optional findIncludingDeleted(@Param("userId") Long userId, + @Param("productId") Long productId); } diff --git a/src/main/java/com/sesac/boheommong/domain/bookmark/service/BookmarkService.java b/src/main/java/com/sesac/boheommong/domain/bookmark/service/BookmarkService.java index fca8a9b..3d4b07c 100644 --- a/src/main/java/com/sesac/boheommong/domain/bookmark/service/BookmarkService.java +++ b/src/main/java/com/sesac/boheommong/domain/bookmark/service/BookmarkService.java @@ -1,15 +1,48 @@ package com.sesac.boheommong.domain.bookmark.service; -import com.sesac.boheommong.domain.bookmark.dto.request.BookmarkRequestDto; import com.sesac.boheommong.domain.bookmark.dto.response.BookmarkResponseDto; import java.util.List; public interface BookmarkService { - BookmarkResponseDto createBookmark(BookmarkRequestDto requestDto); - BookmarkResponseDto getBookmark(Long bookmarkId); - void deleteBookmark(Long bookmarkId); - Boolean getState(String Email, Long boardId); - List getAllBookmarksByUser(Long userId); + /** + * 특정 상품이 북마크 되었는지 여부를 반환 + * @param loginEmail 로그인 이메일 + * @param productId 상품 ID + * @return 북마크 되어 있다면 true, 아니면 false + */ + Boolean getState(String loginEmail, Long productId); + + /** + * 로그인 이메일로 유저를 조회한 뒤 해당 유저의 모든 북마크 목록을 반환 + * @param loginEmail 로그인 이메일 + * @return BookmarkResponseDto 리스트 + */ + List getAllBookmarksByLoginEmail(String loginEmail); + + /** + * 북마크 생성 + * @param loginEmail 로그인 이메일 + * @param productId 북마크할 상품 ID + * @return 생성된 BookmarkResponseDto + */ + BookmarkResponseDto createBookmark(String loginEmail, Long productId); + + /** + * 단일 북마크 조회 + * @param loginEmail 로그인 이메일 + * @param bookmarkId 북마크 ID + * @return 조회된 BookmarkResponseDto + */ + BookmarkResponseDto getBookmark(String loginEmail, Long bookmarkId); + + /** + * 북마크 삭제 (소프트 삭제) + * @param loginEmail 로그인 이메일 + * @param bookmarkId 북마크 ID + */ + void deleteBookmark(String loginEmail, Long bookmarkId); + + void addOrCancelBookmark(String loginEmail, Long productId); } diff --git a/src/main/java/com/sesac/boheommong/domain/bookmark/service/BookmarkServiceImpl.java b/src/main/java/com/sesac/boheommong/domain/bookmark/service/BookmarkServiceImpl.java index d3a6a5f..dcdee47 100644 --- a/src/main/java/com/sesac/boheommong/domain/bookmark/service/BookmarkServiceImpl.java +++ b/src/main/java/com/sesac/boheommong/domain/bookmark/service/BookmarkServiceImpl.java @@ -1,71 +1,109 @@ package com.sesac.boheommong.domain.bookmark.service; -import com.sesac.boheommong.domain.bookmark.dto.request.BookmarkRequestDto; import com.sesac.boheommong.domain.bookmark.dto.response.BookmarkResponseDto; import com.sesac.boheommong.domain.bookmark.entity.Bookmark; import com.sesac.boheommong.domain.bookmark.repository.BookmarkRepository; +import com.sesac.boheommong.domain.bookmark.swagger.*; import com.sesac.boheommong.domain.insurance.entity.InsuranceProduct; import com.sesac.boheommong.domain.insurance.repository.InsuranceProductRepository; import com.sesac.boheommong.domain.user.entity.User; import com.sesac.boheommong.domain.user.repository.UserRepository; +import com.sesac.boheommong.global.exception.BaseException; +import com.sesac.boheommong.global.exception.error.ErrorCode; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import java.util.ArrayList; import java.util.List; +import java.util.Optional; @Service -@RequiredArgsConstructor @Transactional +@RequiredArgsConstructor public class BookmarkServiceImpl implements BookmarkService { private final BookmarkRepository bookmarkRepository; private final UserRepository userRepository; private final InsuranceProductRepository insuranceProductRepository; + /** + * 특정 상품이 북마크되었는지 여부 조회 + */ @Override public Boolean getState(String loginEmail, Long productId) { User user = userRepository.findByLoginEmail(loginEmail) - .orElseThrow(() -> new RuntimeException("유저가 존재하지 않습니다.")); + .orElseThrow(() -> BaseException.from(ErrorCode.USER_NOT_FOUND)); InsuranceProduct product = insuranceProductRepository.findById(productId) - .orElseThrow(() -> new RuntimeException("상품이 존재하지 않습니다.")); + .orElseThrow(() -> BaseException.from(ErrorCode.INSURANCE_PRODUCT_NOT_FOUND)); return bookmarkRepository.findByUserAndProduct(user, product).isPresent(); } + /** + * 로그인 이메일로 유저 조회 후, 유저의 모든 북마크 조회 + */ @Override - @Transactional - public List getAllBookmarksByUser(Long userId) { + public List getAllBookmarksByLoginEmail(String loginEmail) { // 1) 유저 존재 여부 확인 - User user = userRepository.findById(userId) - .orElseThrow(() -> new IllegalArgumentException("해당 유저가 존재하지 않습니다. userId=" + userId)); + User user = userRepository.findByLoginEmail(loginEmail) + .orElseThrow(() -> BaseException.from(ErrorCode.USER_NOT_FOUND)); - // 2) 해당 유저의 모든 북마크 조회 + // 2) 해당 유저의 모든 북마크 조회 (최신 순) List bookmarks = bookmarkRepository.findAllByUserOrderByCreatedAtDesc(user); - // 3) Bookmark 엔티티 -> BookmarkResponseDto 변환 - List result = new ArrayList<>(); - for (Bookmark bookmark : bookmarks) { - result.add(new BookmarkResponseDto( - bookmark.getBookmarkId(), - bookmark.getUser().getUserId(), - bookmark.getProduct().getProductId(), - bookmark.getCreatedAt() - )); - } + // 3) Bookmark -> BookmarkResponseDto 변환 + return bookmarks.stream() + .map(bookmark -> new BookmarkResponseDto( + bookmark.getBookmarkId(), + bookmark.getUser().getUserId(), + bookmark.getProduct().getProductId(), + bookmark.getCreatedAt() + )) + .toList(); + } - return result; + /** + * 북마크 토글 메서드 + * - 이미 존재하면 소프트 삭제 + * - 존재하지 않으면 새로 생성 + */ + @Transactional + @Override + public void addOrCancelBookmark(String loginEmail, Long productId) { + // 1) 유저 조회 + User user = userRepository.findByLoginEmail(loginEmail) + .orElseThrow(() -> BaseException.from(ErrorCode.USER_NOT_FOUND)); + + // 2) 상품 조회 + InsuranceProduct product = insuranceProductRepository.findById(productId) + .orElseThrow(() -> BaseException.from(ErrorCode.INSURANCE_PRODUCT_NOT_FOUND)); + + // 3) 기존 북마크가 있는지 확인 (물리 삭제 방식이므로 소프트 삭제 고려 X) + Optional optionalBookmark = bookmarkRepository.findByUserAndProduct(user, product); + + if (optionalBookmark.isPresent()) { + // (A) 이미 북마크가 존재하면 -> 물리 삭제 + bookmarkRepository.delete(optionalBookmark.get()); + // 실제 DB row가 삭제되므로 중복키 충돌 안 남. + } else { + // (B) 없으면 -> 새 북마크 생성 + Bookmark bookmark = Bookmark.create(user, product); + bookmarkRepository.save(bookmark); + } } + + /** + * 북마크 생성 + */ @Override - public BookmarkResponseDto createBookmark(BookmarkRequestDto requestDto) { - User user = userRepository.findById(requestDto.getUserId()) - .orElseThrow(() -> new IllegalArgumentException("해당 유저가 존재하지 않습니다. userId=" + requestDto.getUserId())); + public BookmarkResponseDto createBookmark(String loginEmail, Long productId) { + User user = userRepository.findByLoginEmail(loginEmail) + .orElseThrow(() -> BaseException.from(ErrorCode.USER_NOT_FOUND)); - InsuranceProduct product = insuranceProductRepository.findById(requestDto.getProductId()) - .orElseThrow(() -> new IllegalArgumentException("해당 보험 상품이 존재하지 않습니다. productId=" + requestDto.getProductId())); + InsuranceProduct product = insuranceProductRepository.findById(productId) + .orElseThrow(() -> BaseException.from(ErrorCode.INSURANCE_PRODUCT_NOT_FOUND)); Bookmark bookmark = Bookmark.create(user, product); Bookmark savedBookmark = bookmarkRepository.save(bookmark); @@ -74,16 +112,29 @@ public BookmarkResponseDto createBookmark(BookmarkRequestDto requestDto) { savedBookmark.getBookmarkId(), savedBookmark.getUser().getUserId(), savedBookmark.getProduct().getProductId(), - savedBookmark.getCreatedAt() // BaseEntity 사용 가정 + savedBookmark.getCreatedAt() ); } - @Transactional + /** + * 북마크 단건 조회 + */ @Override - public BookmarkResponseDto getBookmark(Long bookmarkId) { + public BookmarkResponseDto getBookmark(String loginEmail, Long bookmarkId) { + // 1) 유저 조회 + User user = userRepository.findByLoginEmail(loginEmail) + .orElseThrow(() -> BaseException.from(ErrorCode.USER_NOT_FOUND)); + + // 2) 북마크 조회 Bookmark bookmark = bookmarkRepository.findById(bookmarkId) - .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 북마크입니다. bookmarkId=" + bookmarkId)); + .orElseThrow(() -> BaseException.from(ErrorCode.BOOKMARK_NOT_FOUND)); + // 3) 본인 북마크인지 확인 (옵션) + if (!bookmark.getUser().equals(user)) { + throw BaseException.from(ErrorCode.INVALID_PERMISSION); + } + + // 4) 엔티티 -> Dto 변환 return new BookmarkResponseDto( bookmark.getBookmarkId(), bookmark.getUser().getUserId(), @@ -92,11 +143,26 @@ public BookmarkResponseDto getBookmark(Long bookmarkId) { ); } + /** + * 북마크 삭제(소프트 삭제) + */ @Override - public void deleteBookmark(Long bookmarkId) { + public void deleteBookmark(String loginEmail, Long bookmarkId) { + // 1) 유저 조회 + User user = userRepository.findByLoginEmail(loginEmail) + .orElseThrow(() -> BaseException.from(ErrorCode.USER_NOT_FOUND)); + + // 2) 북마크 조회 Bookmark bookmark = bookmarkRepository.findById(bookmarkId) - .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 북마크입니다. bookmarkId=" + bookmarkId)); + .orElseThrow(() -> BaseException.from(ErrorCode.BOOKMARK_NOT_FOUND)); + + // 3) 본인 북마크인지 확인 (옵션) + if (!bookmark.getUser().equals(user)) { + throw BaseException.from(ErrorCode.INVALID_PERMISSION); + } + // 4) Soft Delete 처리 bookmark.softDelete(); + // BaseEntity나 별도의 @SQLDelete를 통한 업데이트가 적용되도록 구성 } } diff --git a/src/main/java/com/sesac/boheommong/domain/bookmark/swagger/DeleteBookmark.java b/src/main/java/com/sesac/boheommong/domain/bookmark/swagger/DeleteBookmark.java new file mode 100644 index 0000000..673ea50 --- /dev/null +++ b/src/main/java/com/sesac/boheommong/domain/bookmark/swagger/DeleteBookmark.java @@ -0,0 +1,36 @@ +package com.sesac.boheommong.domain.bookmark.swagger; + +import com.sesac.boheommong.global.response.Response; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.http.MediaType; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.lang.annotation.*; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Operation( + summary = "북마크 삭제(소프트 삭제)", + description = "북마크 ID로 특정 북마크를 삭제한다." +) +@ApiResponses({ + @ApiResponse( + responseCode = "200", + description = "북마크 삭제 성공", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = Response.class) + ) + ), + @ApiResponse( + responseCode = "404", + description = "해당 북마크가 존재하지 않거나 권한 없는 경우", + content = @Content(schema = @Schema(implementation = Response.class)) + ) +}) +public @interface DeleteBookmark { +} diff --git a/src/main/java/com/sesac/boheommong/domain/bookmark/swagger/GetAllBookmarks.java b/src/main/java/com/sesac/boheommong/domain/bookmark/swagger/GetAllBookmarks.java new file mode 100644 index 0000000..8d53317 --- /dev/null +++ b/src/main/java/com/sesac/boheommong/domain/bookmark/swagger/GetAllBookmarks.java @@ -0,0 +1,38 @@ +package com.sesac.boheommong.domain.bookmark.swagger; + +import com.sesac.boheommong.domain.bookmark.dto.response.BookmarkResponseDto; +import com.sesac.boheommong.global.response.Response; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.http.MediaType; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.lang.annotation.*; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Operation( + summary = "로그인 사용자의 북마크 전체 조회", + description = "사용자가 북마크한 모든 상품 목록을 최신순으로 조회한다." +) +@ApiResponses({ + @ApiResponse( + responseCode = "200", + description = "북마크 목록 조회 성공", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + // 목록 형태(List)를 표현하려면 아래처럼 배열로 표시: + schema = @Schema(type = "array", implementation = BookmarkResponseDto.class) + ) + ), + @ApiResponse( + responseCode = "401", + description = "인증 실패 (JWT 토큰 불량 등)", + content = @Content(schema = @Schema(implementation = Response.class)) + ) +}) +public @interface GetAllBookmarks { +} diff --git a/src/main/java/com/sesac/boheommong/domain/bookmark/swagger/GetBookmarkById.java b/src/main/java/com/sesac/boheommong/domain/bookmark/swagger/GetBookmarkById.java new file mode 100644 index 0000000..06045c6 --- /dev/null +++ b/src/main/java/com/sesac/boheommong/domain/bookmark/swagger/GetBookmarkById.java @@ -0,0 +1,37 @@ +package com.sesac.boheommong.domain.bookmark.swagger; + +import com.sesac.boheommong.domain.bookmark.dto.response.BookmarkResponseDto; +import com.sesac.boheommong.global.response.Response; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.http.MediaType; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.lang.annotation.*; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Operation( + summary = "단일 북마크 조회", + description = "북마크 ID를 통해 북마크 정보를 단건 조회한다." +) +@ApiResponses({ + @ApiResponse( + responseCode = "200", + description = "북마크 단건 조회 성공", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = BookmarkResponseDto.class) + ) + ), + @ApiResponse( + responseCode = "404", + description = "해당 북마크가 존재하지 않거나 권한 없는 경우", + content = @Content(schema = @Schema(implementation = Response.class)) + ) +}) +public @interface GetBookmarkById { +} diff --git a/src/main/java/com/sesac/boheommong/domain/bookmark/swagger/GetBookmarkState.java b/src/main/java/com/sesac/boheommong/domain/bookmark/swagger/GetBookmarkState.java new file mode 100644 index 0000000..fd2dfc6 --- /dev/null +++ b/src/main/java/com/sesac/boheommong/domain/bookmark/swagger/GetBookmarkState.java @@ -0,0 +1,47 @@ +package com.sesac.boheommong.domain.bookmark.swagger; + +import com.sesac.boheommong.global.response.Response; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.http.MediaType; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.lang.annotation.*; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Operation( + summary = "특정 상품의 북마크 여부 확인", + description = "로그인 사용자가 특정 상품에 대해 북마크했는지 여부를 조회한다." +) +@ApiResponses({ + @ApiResponse( + responseCode = "200", + description = "북마크 여부 조회 성공 (true/false)", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = Boolean.class) + ) + ), + @ApiResponse( + responseCode = "400", + description = "잘못된 요청 (파라미터 누락 등)", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = Response.class) + ) + ), + @ApiResponse( + responseCode = "404", + description = "해당 유저나 상품이 존재하지 않을 경우", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = Response.class) + ) + ) +}) +public @interface GetBookmarkState { +} diff --git a/src/main/java/com/sesac/boheommong/domain/bookmark/swagger/PostCreateBookmark.java b/src/main/java/com/sesac/boheommong/domain/bookmark/swagger/PostCreateBookmark.java new file mode 100644 index 0000000..d568624 --- /dev/null +++ b/src/main/java/com/sesac/boheommong/domain/bookmark/swagger/PostCreateBookmark.java @@ -0,0 +1,37 @@ +package com.sesac.boheommong.domain.bookmark.swagger; + +import com.sesac.boheommong.domain.bookmark.dto.response.BookmarkResponseDto; +import com.sesac.boheommong.global.response.Response; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.http.MediaType; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.lang.annotation.*; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Operation( + summary = "북마크 생성", + description = "상품을 북마크한다." +) +@ApiResponses({ + @ApiResponse( + responseCode = "200", + description = "북마크 생성 성공", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = BookmarkResponseDto.class) + ) + ), + @ApiResponse( + responseCode = "404", + description = "해당 상품 or 유저가 존재하지 않는 경우", + content = @Content(schema = @Schema(implementation = Response.class)) + ) +}) +public @interface PostCreateBookmark { +} diff --git a/src/main/java/com/sesac/boheommong/domain/bookmark/swagger/PostToggleBookmark.java b/src/main/java/com/sesac/boheommong/domain/bookmark/swagger/PostToggleBookmark.java new file mode 100644 index 0000000..f5f561d --- /dev/null +++ b/src/main/java/com/sesac/boheommong/domain/bookmark/swagger/PostToggleBookmark.java @@ -0,0 +1,39 @@ +package com.sesac.boheommong.domain.bookmark.swagger; + +import com.sesac.boheommong.global.response.Response; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.http.MediaType; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.lang.annotation.*; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Operation( + summary = "상품 북마크 추가/취소 토글", + description = "이미 북마크가 없으면 추가, 이미 있다면 북마크 취소(소프트 삭제)." +) +@ApiResponses({ + @ApiResponse( + responseCode = "200", + description = "북마크 토글 성공", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = Response.class) + ) + ), + @ApiResponse( + responseCode = "404", + description = "해당 상품 또는 유저가 존재하지 않는 경우", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = Response.class) + ) + ) +}) +public @interface PostToggleBookmark { +} diff --git a/src/main/java/com/sesac/boheommong/global/entity/BaseEntity.java b/src/main/java/com/sesac/boheommong/global/entity/BaseEntity.java index 36c09a6..79fca71 100644 --- a/src/main/java/com/sesac/boheommong/global/entity/BaseEntity.java +++ b/src/main/java/com/sesac/boheommong/global/entity/BaseEntity.java @@ -30,4 +30,9 @@ public void softDelete() { this.isDeleted = true; this.deletedAt = LocalDateTime.now(); } + + public void restore() { + this.isDeleted = false; + this.deletedAt = null; + } } diff --git a/src/main/java/com/sesac/boheommong/global/exception/error/ErrorCode.java b/src/main/java/com/sesac/boheommong/global/exception/error/ErrorCode.java index 7737ac6..ffdaeba 100644 --- a/src/main/java/com/sesac/boheommong/global/exception/error/ErrorCode.java +++ b/src/main/java/com/sesac/boheommong/global/exception/error/ErrorCode.java @@ -21,7 +21,11 @@ public enum ErrorCode { NOTIFICATION_NOT_FOUND("NOTIFICATION-0002", "해당 알림이 존재하지 않습니다.", ErrorDisplayType.POPUP), // insurance product - INSURANCE_PRODUCT_NOT_FOUND("INS-0001", "해당 보험 상품이 존재하지 않습니다.", ErrorDisplayType.POPUP) + INSURANCE_PRODUCT_NOT_FOUND("INS-0001", "해당 보험 상품이 존재하지 않습니다.", ErrorDisplayType.POPUP), + + // bookmark + BOOKMARK_NOT_FOUND("BOOKMARK-0001", "해당 북마크가 존재하지 않습니다.", ErrorDisplayType.POPUP), + ; private final String code;