diff --git a/.github/workflows/cd-workflow-dev.yml b/.github/workflows/cd-workflow-dev.yml index 5a04c00db..dfbaccd1a 100644 --- a/.github/workflows/cd-workflow-dev.yml +++ b/.github/workflows/cd-workflow-dev.yml @@ -68,7 +68,9 @@ jobs: cd ${{ env.COMPOSE_PATH }} echo "βœ‹πŸ»Stopping existing container and Cleaning up old images" - sudo docker-compose down --rmi all +# sudo docker-compose down --rmi all + sudo docker-compose stop ${{ secrets.DOCKER_IMAGE }} + sudo docker rm -f ${{ secrets.DOCKER_IMAGE }} sudo docker ps -a diff --git a/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/SavedBookJpaRepository.java b/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/SavedBookJpaRepository.java index 234b9bfd4..6038ecc7d 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/SavedBookJpaRepository.java +++ b/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/SavedBookJpaRepository.java @@ -4,11 +4,12 @@ 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 SavedBookJpaRepository extends JpaRepository { @Query("SELECT CASE WHEN COUNT(s) > 0 THEN true ELSE false END FROM SavedBookJpaEntity s " + "WHERE s.userJpaEntity.userId = :userId AND s.bookJpaEntity.bookId = :bookId") - boolean existsByUserIdAndBookId(Long userId, Long bookId); + boolean existsByUserIdAndBookId(@Param("userId") Long userId, @Param("bookId") Long bookId); @Modifying(clearAutomatically = true, flushAutomatically = true) @Query("DELETE FROM SavedBookJpaEntity s WHERE s.userJpaEntity.userId = :userId AND s.bookJpaEntity.bookId = :bookId") diff --git a/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java b/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java index 0c4d9577b..ef76ea025 100644 --- a/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java +++ b/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java @@ -156,7 +156,6 @@ public enum ErrorCode implements ResponseCode { TAG_NOT_FOUND(HttpStatus.NOT_FOUND, 160002, "μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” TAG μž…λ‹ˆλ‹€."), INVALID_FEED_COMMAND(HttpStatus.BAD_REQUEST, 160003, "μœ νš¨ν•˜μ§€ μ•Šμ€ FEED 생성/μˆ˜μ • μš”μ²­ μž…λ‹ˆλ‹€."), FEED_ACCESS_FORBIDDEN(HttpStatus.FORBIDDEN, 160004, "ν”Όλ“œ μ ‘κ·Ό κΆŒν•œμ΄ μ—†μŠ΅λ‹ˆλ‹€."), - DUPLICATED_FEEDS_IN_COLLECTION(HttpStatus.INTERNAL_SERVER_ERROR, 160005, "μ€‘λ³΅λœ ν”Όλ“œκ°€ μ‘΄μž¬ν•©λ‹ˆλ‹€."), FEED_ALREADY_SAVED(HttpStatus.BAD_REQUEST, 160006, "μ‚¬μš©μžκ°€ 이미 μ €μž₯ν•œ ν”Όλ“œμž…λ‹ˆλ‹€."), FEED_NOT_SAVED_CANNOT_DELETE(HttpStatus.BAD_REQUEST, 160007, "μ‚¬μš©μžκ°€ μ €μž₯ν•˜μ§€ μ•Šμ€ ν”Όλ“œλŠ” μ €μž₯μ‚­μ œ ν•  수 μ—†μŠ΅λ‹ˆλ‹€."), FEED_CAN_NOT_SHOW_PRIVATE_ONE(HttpStatus.BAD_REQUEST, 160008, "λΉ„κ³΅κ°œ ν”Όλ“œλŠ” ν”Όλ“œ μž‘μ„±μž μ΄μ™Έμ—λŠ” μ‘°νšŒν•  수 μ—†μŠ΅λ‹ˆλ‹€."), diff --git a/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java b/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java index 1f39070ce..ea880cef8 100644 --- a/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java @@ -1,34 +1,22 @@ package konkuk.thip.feed.adapter.out.persistence; import konkuk.thip.common.entity.StatusType; -import konkuk.thip.common.exception.EntityNotFoundException; import konkuk.thip.common.util.Cursor; import konkuk.thip.common.util.CursorBasedList; -import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; -import konkuk.thip.feed.adapter.out.jpa.SavedFeedJpaEntity; -import konkuk.thip.feed.adapter.out.jpa.TagJpaEntity; import konkuk.thip.feed.adapter.out.mapper.FeedMapper; import konkuk.thip.feed.adapter.out.persistence.repository.FeedJpaRepository; import konkuk.thip.feed.adapter.out.persistence.repository.FeedTag.FeedTagJpaRepository; import konkuk.thip.feed.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.feed.application.port.out.FeedQueryPort; import konkuk.thip.feed.application.port.out.dto.TagCategoryQueryDto; -import konkuk.thip.feed.application.port.out.dto.FeedIdAndTagProjection; import konkuk.thip.feed.application.port.out.dto.FeedQueryDto; -import konkuk.thip.feed.domain.Feed; -import konkuk.thip.feed.domain.SavedFeeds; -import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import java.time.LocalDateTime; import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; - -import static konkuk.thip.common.exception.code.ErrorCode.USER_NOT_FOUND; @Repository @RequiredArgsConstructor @@ -112,40 +100,13 @@ public int countPublicFeedsByUserId(Long userId) { } @Override - public SavedFeeds findSavedFeedsByUserId(Long userId) { - UserJpaEntity user = userJpaRepository.findById(userId) - .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); - - List savedFeedEntities = - savedFeedJpaRepository.findAllByUserId(user.getUserId()); - - List feedIds = savedFeedEntities.stream() - .map(entity -> entity.getFeedJpaEntity().getPostId()) - .toList(); - - // Projection 기반 쑰회 - List results = feedTagJpaRepository.findFeedIdAndTagsByFeedIds(feedIds); - - Map> feedTagsMap = results.stream() - .collect(Collectors.groupingBy( - FeedIdAndTagProjection::getFeedId, - Collectors.mapping(FeedIdAndTagProjection::getTagJpaEntity, Collectors.toList()) - )); - - List feeds = savedFeedEntities.stream() - .map(entity -> { - FeedJpaEntity feedJpa = entity.getFeedJpaEntity(); - List tags = feedTagsMap.getOrDefault(feedJpa.getPostId(), List.of()); - return feedMapper.toDomainEntity(feedJpa, tags); - }) - .toList(); - - return new SavedFeeds(feeds); + public Set findSavedFeedIdsByUserIdAndFeedIds(Set feedIds, Long userId) { + return savedFeedJpaRepository.findSavedFeedIdsByUserIdAndFeedIds(userId, feedIds); } @Override - public Set findSavedFeedIdsByUserIdAndFeedIds(Set feedIds, Long userId) { - return savedFeedJpaRepository.findSavedFeedIdsByUserIdAndFeedIds(userId, feedIds); + public boolean existsSavedFeedByUserIdAndFeedId(Long userId, Long feedId) { + return savedFeedJpaRepository.existsByUserIdAndFeedId(userId, feedId); } @Override diff --git a/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/SavedFeedJpaRepository.java b/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/SavedFeedJpaRepository.java index 9e4824066..c10a53f7d 100644 --- a/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/SavedFeedJpaRepository.java +++ b/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/SavedFeedJpaRepository.java @@ -24,4 +24,8 @@ public interface SavedFeedJpaRepository extends JpaRepository 0 THEN true ELSE false END FROM SavedFeedJpaEntity s " + + "WHERE s.userJpaEntity.userId = :userId AND s.feedJpaEntity.postId = :feedId") + boolean existsByUserIdAndFeedId(@Param("userId") Long userId, @Param("feedId") Long feedId); } diff --git a/src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java b/src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java index aa71dc8b8..b4d7a903d 100644 --- a/src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java +++ b/src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java @@ -4,7 +4,6 @@ import konkuk.thip.common.util.CursorBasedList; import konkuk.thip.feed.application.port.out.dto.TagCategoryQueryDto; import konkuk.thip.feed.application.port.out.dto.FeedQueryDto; -import konkuk.thip.feed.domain.SavedFeeds; import java.util.List; import java.util.Set; @@ -36,11 +35,10 @@ public interface FeedQueryPort { /** * μ €μž₯된 ν”Όλ“œ 쑰회 */ - SavedFeeds findSavedFeedsByUserId(Long userId); Set findSavedFeedIdsByUserIdAndFeedIds(Set feedIds, Long userId); - List findAllTags(); + boolean existsSavedFeedByUserIdAndFeedId(Long userId, Long feedId); /** * νŠΉμ • μ±…μœΌλ‘œ μž‘μ„±λœ ν”Όλ“œ 쑰회 @@ -50,4 +48,9 @@ public interface FeedQueryPort { CursorBasedList findFeedsByBookIsbnOrderByLatest(String isbn, Long userId, Cursor cursor); List findLatestPublicFeedCreatorsIn(Set userIds, int size); + + /** + * λͺ¨λ“  νƒœκ·Έ 쑰회 + */ + List findAllTags(); } diff --git a/src/main/java/konkuk/thip/feed/application/service/FeedSavedService.java b/src/main/java/konkuk/thip/feed/application/service/FeedSavedService.java index 80116c054..0169faa54 100644 --- a/src/main/java/konkuk/thip/feed/application/service/FeedSavedService.java +++ b/src/main/java/konkuk/thip/feed/application/service/FeedSavedService.java @@ -1,16 +1,18 @@ package konkuk.thip.feed.application.service; +import konkuk.thip.common.exception.BusinessException; import konkuk.thip.feed.application.port.in.FeedSavedUseCase; import konkuk.thip.feed.application.port.in.dto.FeedIsSavedCommand; import konkuk.thip.feed.application.port.in.dto.FeedIsSavedResult; import konkuk.thip.feed.application.port.out.FeedCommandPort; import konkuk.thip.feed.application.port.out.FeedQueryPort; import konkuk.thip.feed.domain.Feed; -import konkuk.thip.feed.domain.SavedFeeds; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import static konkuk.thip.common.exception.code.ErrorCode.*; + @Service @RequiredArgsConstructor public class FeedSavedService implements FeedSavedUseCase { @@ -20,24 +22,31 @@ public class FeedSavedService implements FeedSavedUseCase { @Override @Transactional - public FeedIsSavedResult changeSavedFeed(FeedIsSavedCommand feedIsSavedCommand) { + public FeedIsSavedResult changeSavedFeed(FeedIsSavedCommand command) { // 1. ν”Όλ“œ 검증 및 쑰회 - Feed feed = feedCommandPort.getByIdOrThrow(feedIsSavedCommand.feedId()); + Feed feed = feedCommandPort.getByIdOrThrow(command.feedId()); - // 2. μœ μ €κ°€ μ €μž₯ν•œ ν”Όλ“œ λͺ©λ‘ 쑰회 - SavedFeeds savedFeeds = feedQueryPort.findSavedFeedsByUserId(feedIsSavedCommand.userId()); + // 2. μœ μ €κ°€ ν•΄λ‹Ή ν”Όλ“œλ₯Ό μ €μž₯ν–ˆλŠ”μ§€ μ—¬λΆ€ 쑰회 + boolean alreadySaved = feedQueryPort.existsSavedFeedByUserIdAndFeedId(command.userId(), feed.getId()); + validateSaveFeedAction(command.isSaved(), alreadySaved); - if (feedIsSavedCommand.isSaved()) { - // μ €μž₯ μš”μ²­ μ‹œ 이미 μ €μž₯λ˜μ–΄ 있으면 μ˜ˆμ™Έ λ°œμƒ - savedFeeds.validateNotAlreadySaved(feed); - feedCommandPort.saveSavedFeed(feedIsSavedCommand.userId(), feed.getId()); + if (command.isSaved()) { + feedCommandPort.saveSavedFeed(command.userId(), feed.getId()); } else { - // μ‚­μ œ μš”μ²­ μ‹œ μ €μž₯λ˜μ–΄ μžˆμ§€ μ•ŠμœΌλ©΄ μ˜ˆμ™Έ λ°œμƒ - savedFeeds.validateCanDelete(feed); - feedCommandPort.deleteSavedFeed(feedIsSavedCommand.userId(), feed.getId()); + feedCommandPort.deleteSavedFeed(command.userId(), feed.getId()); } - return FeedIsSavedResult.of(feed.getId(), feedIsSavedCommand.isSaved()); + return FeedIsSavedResult.of(feed.getId(), command.isSaved()); + } + + private void validateSaveFeedAction(boolean isSaveRequest, boolean alreadySaved) { + if (isSaveRequest && alreadySaved) { + // 이미 μ €μž₯λ˜μ–΄ μžˆλŠ” ν”Όλ“œλ₯Ό λ‹€μ‹œ μ €μž₯ν•˜λ €λŠ” 경우 μ˜ˆμ™Έ 처리 + throw new BusinessException(FEED_ALREADY_SAVED); + } else if (!isSaveRequest && !alreadySaved) { + // μ €μž₯λ˜μ§€ μ•Šμ€ ν”Όλ“œλ₯Ό μ‚­μ œν•˜λ €λŠ” 경우 μ˜ˆμ™Έ 처리 + throw new BusinessException(FEED_NOT_SAVED_CANNOT_DELETE); + } } } diff --git a/src/main/java/konkuk/thip/feed/domain/SavedFeeds.java b/src/main/java/konkuk/thip/feed/domain/SavedFeeds.java deleted file mode 100644 index e5c739477..000000000 --- a/src/main/java/konkuk/thip/feed/domain/SavedFeeds.java +++ /dev/null @@ -1,42 +0,0 @@ -package konkuk.thip.feed.domain; - -import konkuk.thip.common.exception.InvalidStateException; -import lombok.Getter; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import static konkuk.thip.common.exception.code.ErrorCode.*; - -@Getter -public class SavedFeeds { - - private final Set feeds; - - public SavedFeeds(List feeds) { - Set feedSet = new HashSet<>(feeds); - if (feedSet.size() != feeds.size()) { - throw new InvalidStateException(DUPLICATED_FEEDS_IN_COLLECTION); - } - this.feeds = Collections.unmodifiableSet(feedSet); - } - - // 쀑볡 μ €μž₯ 검증 - public void validateNotAlreadySaved(Feed feed) { - if (feeds.contains(feed)) { - throw new InvalidStateException(FEED_ALREADY_SAVED); - } - } - - // μ‚­μ œ κ°€λŠ₯ μ—¬λΆ€ 검증 - public void validateCanDelete(Feed feed) { - if (!feeds.contains(feed)) { - throw new InvalidStateException(FEED_NOT_SAVED_CANNOT_DELETE); - } - } - -} - -