diff --git a/src/main/java/org/hankki/hankkiserver/api/favorite/controller/FavoriteController.java b/src/main/java/org/hankki/hankkiserver/api/favorite/controller/FavoriteController.java new file mode 100644 index 00000000..dba6479f --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/favorite/controller/FavoriteController.java @@ -0,0 +1,38 @@ +package org.hankki.hankkiserver.api.favorite.controller; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.hankki.hankkiserver.api.dto.HankkiResponse; +import org.hankki.hankkiserver.api.favorite.controller.request.FavoriteDeleteRequest; +import org.hankki.hankkiserver.api.favorite.service.FavoriteCommandService; +import org.hankki.hankkiserver.api.favorite.service.command.FavoritePostCommand; +import org.hankki.hankkiserver.api.favorite.controller.request.FavoritePostRequest; +import org.hankki.hankkiserver.api.favorite.service.command.FavoritesDeleteCommand; +import org.hankki.hankkiserver.auth.UserId; +import org.hankki.hankkiserver.common.code.CommonSuccessCode; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1") +public class FavoriteController { + + private final FavoriteCommandService favoriteCommandService; + + @PostMapping("/favorites") + public HankkiResponse createFavorite(@UserId final Long userId, @RequestBody @Valid final FavoritePostRequest request) { + + favoriteCommandService.create(FavoritePostCommand.of(userId, request)); + return HankkiResponse.success(CommonSuccessCode.CREATED); + } + + @PostMapping("/favorites/batch-delete") + public HankkiResponse deleteFavorite(@UserId final Long userId, @RequestBody final FavoriteDeleteRequest request) { + + favoriteCommandService.deleteFavorites(FavoritesDeleteCommand.of(userId, request)); + return HankkiResponse.success(CommonSuccessCode.NO_CONTENT); + } +} \ No newline at end of file diff --git a/src/main/java/org/hankki/hankkiserver/api/favorite/controller/request/FavoriteDeleteRequest.java b/src/main/java/org/hankki/hankkiserver/api/favorite/controller/request/FavoriteDeleteRequest.java new file mode 100644 index 00000000..62e28bb0 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/favorite/controller/request/FavoriteDeleteRequest.java @@ -0,0 +1,8 @@ +package org.hankki.hankkiserver.api.favorite.controller.request; + +import java.util.List; + +public record FavoriteDeleteRequest( + List favoriteIds +) { +} diff --git a/src/main/java/org/hankki/hankkiserver/api/favorite/controller/request/FavoritePostRequest.java b/src/main/java/org/hankki/hankkiserver/api/favorite/controller/request/FavoritePostRequest.java new file mode 100644 index 00000000..12bca240 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/favorite/controller/request/FavoritePostRequest.java @@ -0,0 +1,14 @@ +package org.hankki.hankkiserver.api.favorite.controller.request; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import java.util.List; + +public record FavoritePostRequest( + @NotBlank(message = "제목이 비었습니다.") + @Size(max = 18, message = "제목 길이가 18자를 초과했습니다.") + String title, + @Size(min = 1, max = 2, message = "해시태그 리스트 size가 1 이상 2 이하가 아닙니다.") + List<@Size(min = 2, max= 10, message = "해시태그가 # 포함 10자를 초과했습니다.") String> details +) { +} diff --git a/src/main/java/org/hankki/hankkiserver/api/favorite/service/FavoriteCommandService.java b/src/main/java/org/hankki/hankkiserver/api/favorite/service/FavoriteCommandService.java new file mode 100644 index 00000000..5e827ee1 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/favorite/service/FavoriteCommandService.java @@ -0,0 +1,50 @@ +package org.hankki.hankkiserver.api.favorite.service; + +import jakarta.transaction.Transactional; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.hankki.hankkiserver.api.auth.service.UserFinder; +import org.hankki.hankkiserver.api.favorite.service.command.FavoritesDeleteCommand; +import org.hankki.hankkiserver.api.favoritestore.service.FavoriteStoreDeleter; +import org.hankki.hankkiserver.common.code.UserErrorCode; +import org.hankki.hankkiserver.common.exception.UnauthorizedException; +import org.hankki.hankkiserver.domain.favorite.model.Favorite; +import org.hankki.hankkiserver.api.favorite.service.command.FavoritePostCommand; +import org.hankki.hankkiserver.domain.user.model.User; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class FavoriteCommandService { + + private final UserFinder userFinder; + private final FavoriteFinder favoriteFinder; + private final FavoriteUpdater favoriteUpdater; + private final FavoriteStoreDeleter favoriteStoreDeleter; + private final FavoriteDeleter favoriteDeleter; + + @Transactional + public Long create(final FavoritePostCommand command) { + + User findUser = userFinder.getUser(command.userId()); + String title = command.title(); + String details = String.join(" ", command.details()); + + return favoriteUpdater.save(Favorite.create(findUser, title, details)); + } + + @Transactional + public void deleteFavorites(final FavoritesDeleteCommand command) { + + List favorites = favoriteFinder.findAllByIds(command.favoriteIds()); + + favorites.forEach(favorite -> { + if (!favorite.getUser().getId().equals(command.userId())) { + throw new UnauthorizedException(UserErrorCode.USER_FORBIDDEN); + } + }); + + favoriteStoreDeleter.deleteAllByFavorites(favorites); + favoriteDeleter.deleteAll(favorites); + } +} \ No newline at end of file diff --git a/src/main/java/org/hankki/hankkiserver/api/favorite/service/FavoriteDeleter.java b/src/main/java/org/hankki/hankkiserver/api/favorite/service/FavoriteDeleter.java new file mode 100644 index 00000000..4389ff88 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/favorite/service/FavoriteDeleter.java @@ -0,0 +1,25 @@ +package org.hankki.hankkiserver.api.favorite.service; + +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.hankki.hankkiserver.common.code.BusinessErrorCode; +import org.hankki.hankkiserver.common.exception.InternalServerException; +import org.hankki.hankkiserver.domain.favorite.model.Favorite; +import org.hankki.hankkiserver.domain.favorite.repository.FavoriteRepository; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class FavoriteDeleter { + + private final FavoriteRepository favoriteRepository; + + protected void deleteAll(List favorites) { + + try { + favoriteRepository.deleteAll(favorites); + } catch (Exception e) { + throw new InternalServerException(BusinessErrorCode.INTERNAL_SERVER_ERROR); + } + } +} diff --git a/src/main/java/org/hankki/hankkiserver/api/favorite/service/FavoriteFinder.java b/src/main/java/org/hankki/hankkiserver/api/favorite/service/FavoriteFinder.java new file mode 100644 index 00000000..c0d3d93e --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/favorite/service/FavoriteFinder.java @@ -0,0 +1,24 @@ +package org.hankki.hankkiserver.api.favorite.service; + +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.hankki.hankkiserver.common.code.FavoriteErrorCode; +import org.hankki.hankkiserver.common.exception.NotFoundException; +import org.hankki.hankkiserver.domain.favorite.model.Favorite; +import org.hankki.hankkiserver.domain.favorite.repository.FavoriteRepository; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class FavoriteFinder { + + private final FavoriteRepository favoriteRepository; + + public Favorite findById(final Long id) { + return favoriteRepository.findById(id).orElseThrow(() -> new NotFoundException(FavoriteErrorCode.FAVORITE_NOT_FOUND)); + } + + public List findAllByIds(final List ids) { + return favoriteRepository.findAllByIds(ids); + } +} diff --git a/src/main/java/org/hankki/hankkiserver/api/favorite/service/FavoriteUpdater.java b/src/main/java/org/hankki/hankkiserver/api/favorite/service/FavoriteUpdater.java new file mode 100644 index 00000000..3f771d7a --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/favorite/service/FavoriteUpdater.java @@ -0,0 +1,20 @@ +package org.hankki.hankkiserver.api.favorite.service; + +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.hankki.hankkiserver.common.code.BusinessErrorCode; +import org.hankki.hankkiserver.common.exception.InternalServerException; +import org.hankki.hankkiserver.domain.favorite.model.Favorite; +import org.hankki.hankkiserver.domain.favorite.repository.FavoriteRepository; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class FavoriteUpdater { + + private final FavoriteRepository favoriteRepository; + + protected Long save(Favorite favorite) { + return favoriteRepository.save(favorite).getId(); + } +} diff --git a/src/main/java/org/hankki/hankkiserver/api/favorite/service/command/FavoritePostCommand.java b/src/main/java/org/hankki/hankkiserver/api/favorite/service/command/FavoritePostCommand.java new file mode 100644 index 00000000..f85fbd37 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/favorite/service/command/FavoritePostCommand.java @@ -0,0 +1,17 @@ +package org.hankki.hankkiserver.api.favorite.service.command; + +import java.util.List; +import org.hankki.hankkiserver.api.favorite.controller.request.FavoritePostRequest; + +public record FavoritePostCommand( + Long userId, + String title, + List details +) { + + public static FavoritePostCommand of(final Long userId, final FavoritePostRequest favoritePostRequest) { + + return new FavoritePostCommand(userId, favoritePostRequest.title(), favoritePostRequest.details()); + + } +} diff --git a/src/main/java/org/hankki/hankkiserver/api/favorite/service/command/FavoritesDeleteCommand.java b/src/main/java/org/hankki/hankkiserver/api/favorite/service/command/FavoritesDeleteCommand.java new file mode 100644 index 00000000..cb952137 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/favorite/service/command/FavoritesDeleteCommand.java @@ -0,0 +1,14 @@ +package org.hankki.hankkiserver.api.favorite.service.command; + +import java.util.List; +import org.hankki.hankkiserver.api.favorite.controller.request.FavoriteDeleteRequest; + +public record FavoritesDeleteCommand( + Long userId, + List favoriteIds +) { + + public static FavoritesDeleteCommand of(final Long userId, final FavoriteDeleteRequest favoriteDeleteRequest) { + return new FavoritesDeleteCommand(userId, favoriteDeleteRequest.favoriteIds()); + } +} diff --git a/src/main/java/org/hankki/hankkiserver/api/favoritestore/service/FavoriteStoreDeleter.java b/src/main/java/org/hankki/hankkiserver/api/favoritestore/service/FavoriteStoreDeleter.java new file mode 100644 index 00000000..f9f09006 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/favoritestore/service/FavoriteStoreDeleter.java @@ -0,0 +1,25 @@ +package org.hankki.hankkiserver.api.favoritestore.service; + +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.hankki.hankkiserver.common.code.BusinessErrorCode; +import org.hankki.hankkiserver.common.exception.InternalServerException; +import org.hankki.hankkiserver.domain.favorite.model.Favorite; +import org.hankki.hankkiserver.domain.favoritestore.repository.FavoriteStoreRepository; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class FavoriteStoreDeleter { + + private final FavoriteStoreRepository favoriteStoreRepository; + + public void deleteAllByFavorites(List favorites) { + + try { + favoriteStoreRepository.deleteAllByFavorites(favorites); + } catch (Exception e) { + throw new InternalServerException(BusinessErrorCode.INTERNAL_SERVER_ERROR); + } + } +} diff --git a/src/main/java/org/hankki/hankkiserver/common/code/FavoriteErrorCode.java b/src/main/java/org/hankki/hankkiserver/common/code/FavoriteErrorCode.java new file mode 100644 index 00000000..c2851e8e --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/common/code/FavoriteErrorCode.java @@ -0,0 +1,15 @@ +package org.hankki.hankkiserver.common.code; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@Getter +@RequiredArgsConstructor +public enum FavoriteErrorCode implements ErrorCode { + + FAVORITE_NOT_FOUND(HttpStatus.NOT_FOUND, "등록되지 않은 족보입니다."); + + private final HttpStatus httpStatus; + private final String message; +} diff --git a/src/main/java/org/hankki/hankkiserver/common/code/FavoriteStoreErrorCode.java b/src/main/java/org/hankki/hankkiserver/common/code/FavoriteStoreErrorCode.java new file mode 100644 index 00000000..36f6caed --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/common/code/FavoriteStoreErrorCode.java @@ -0,0 +1,15 @@ +package org.hankki.hankkiserver.common.code; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@Getter +@RequiredArgsConstructor +public enum FavoriteStoreErrorCode implements ErrorCode { + + FAVORITE_STORE_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 데이터입니다."); + + private final HttpStatus httpStatus; + private final String message; +} diff --git a/src/main/java/org/hankki/hankkiserver/common/code/UserErrorCode.java b/src/main/java/org/hankki/hankkiserver/common/code/UserErrorCode.java index 05b14ec7..46a683d8 100644 --- a/src/main/java/org/hankki/hankkiserver/common/code/UserErrorCode.java +++ b/src/main/java/org/hankki/hankkiserver/common/code/UserErrorCode.java @@ -9,7 +9,8 @@ public enum UserErrorCode implements ErrorCode { USER_NOT_FOUND(HttpStatus.NOT_FOUND, "등록되지 않은 회원입니다."), - USER_INFO_NOT_FOUND(HttpStatus.NOT_FOUND, "회원 정보를 찾을 수 없습니다."); + USER_INFO_NOT_FOUND(HttpStatus.NOT_FOUND, "회원 정보를 찾을 수 없습니다."), + USER_FORBIDDEN(HttpStatus.FORBIDDEN, "리소스에 대한 권한이 없는 회원입니다."); private final HttpStatus httpStatus; private final String message; diff --git a/src/main/java/org/hankki/hankkiserver/domain/favorite/model/Favorite.java b/src/main/java/org/hankki/hankkiserver/domain/favorite/model/Favorite.java index 8522f7e6..c7f97a10 100644 --- a/src/main/java/org/hankki/hankkiserver/domain/favorite/model/Favorite.java +++ b/src/main/java/org/hankki/hankkiserver/domain/favorite/model/Favorite.java @@ -1,10 +1,14 @@ package org.hankki.hankkiserver.domain.favorite.model; import jakarta.persistence.*; +import java.util.ArrayList; +import java.util.List; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import org.hankki.hankkiserver.domain.common.BaseTimeEntity; +import org.hankki.hankkiserver.domain.favoritestore.model.FavoriteStore; import org.hankki.hankkiserver.domain.user.model.User; @Entity @@ -27,6 +31,24 @@ public class Favorite extends BaseTimeEntity { private String detail; @Column(nullable = false) - private String favorite_image_url; - + private String image_url; + + @OneToMany(mappedBy = "favorite") + private List favoriteStores = new ArrayList<>(); + + public static Favorite create(User user, String name, String detail) { + return Favorite.builder() + .user(user) + .name(name) + .detail(detail) + .build(); + } + + @Builder + private Favorite(User user, String name, String detail) { + this.user = user; + this.name = name; + this.detail = detail; + this.image_url = "default.com"; + } } diff --git a/src/main/java/org/hankki/hankkiserver/domain/favorite/repository/FavoriteRepository.java b/src/main/java/org/hankki/hankkiserver/domain/favorite/repository/FavoriteRepository.java index 07d21f28..ed703df3 100644 --- a/src/main/java/org/hankki/hankkiserver/domain/favorite/repository/FavoriteRepository.java +++ b/src/main/java/org/hankki/hankkiserver/domain/favorite/repository/FavoriteRepository.java @@ -1,7 +1,18 @@ package org.hankki.hankkiserver.domain.favorite.repository; +import java.util.List; import org.hankki.hankkiserver.domain.favorite.model.Favorite; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface FavoriteRepository extends JpaRepository { + + @Modifying + @Query("delete from Favorite f where f in :favorites") + void deleteAll(@Param("favorites") List favorites); + + @Query("select f from Favorite f where f.id in :favoriteId") + List findAllByIds(@Param("favoriteId") List favoriteId); } diff --git a/src/main/java/org/hankki/hankkiserver/domain/favoritestore/model/FavoriteStore.java b/src/main/java/org/hankki/hankkiserver/domain/favoritestore/model/FavoriteStore.java index 3550fc07..df81187a 100644 --- a/src/main/java/org/hankki/hankkiserver/domain/favoritestore/model/FavoriteStore.java +++ b/src/main/java/org/hankki/hankkiserver/domain/favoritestore/model/FavoriteStore.java @@ -6,9 +6,11 @@ import lombok.NoArgsConstructor; import org.hankki.hankkiserver.domain.favorite.model.Favorite; import org.hankki.hankkiserver.domain.store.model.Store; +import org.hibernate.annotations.BatchSize; @Entity @Getter +@BatchSize(size = 100) @NoArgsConstructor(access = AccessLevel.PROTECTED) public class FavoriteStore { diff --git a/src/main/java/org/hankki/hankkiserver/domain/favoritestore/repository/FavoriteStoreRepository.java b/src/main/java/org/hankki/hankkiserver/domain/favoritestore/repository/FavoriteStoreRepository.java index 2340b52f..808d1ba5 100644 --- a/src/main/java/org/hankki/hankkiserver/domain/favoritestore/repository/FavoriteStoreRepository.java +++ b/src/main/java/org/hankki/hankkiserver/domain/favoritestore/repository/FavoriteStoreRepository.java @@ -1,7 +1,16 @@ package org.hankki.hankkiserver.domain.favoritestore.repository; +import java.util.List; +import org.hankki.hankkiserver.domain.favorite.model.Favorite; import org.hankki.hankkiserver.domain.favoritestore.model.FavoriteStore; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface FavoriteStoreRepository extends JpaRepository { + + @Modifying + @Query("delete from FavoriteStore fs where fs.favorite in :favorites") + void deleteAllByFavorites(@Param("favorites")List favorites); }