From 5b0d76d6c62e462e1f77213148476b055338d6ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Fri, 8 Aug 2025 23:32:06 +0900 Subject: [PATCH 01/26] =?UTF-8?q?[feat]=20=EA=B4=80=EB=A0=A8=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/konkuk/thip/common/exception/code/ErrorCode.java | 1 + 1 file changed, 1 insertion(+) 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 4d6b735da..017a761ec 100644 --- a/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java +++ b/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java @@ -120,6 +120,7 @@ public enum ErrorCode implements ResponseCode { RECORD_NOT_FOUND(HttpStatus.NOT_FOUND, 130000, "존재하지 않는 RECORD 입니다."), RECORD_CANNOT_BE_OVERVIEW(HttpStatus.BAD_REQUEST, 130001, "총평이 될 수 없는 RECORD 입니다."), INVALID_RECORD_PAGE_RANGE(HttpStatus.BAD_REQUEST, 130002, "RECORD의 page 값이 유효하지 않습니다."), + RECORD_ACCESS_FORBIDDEN(HttpStatus.FORBIDDEN, 130003, "기록 접근 권한이 없습니다."), /** * 140000 : roomParticipant error From 25ba9bade6d392092efc4697f4704f2f46f7b3dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Fri, 8 Aug 2025 23:32:40 +0900 Subject: [PATCH 02/26] =?UTF-8?q?[feat]=20Record=20=EC=82=AD=EC=A0=9C?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EA=B2=80=EC=A6=9D=20=EC=B6=94=EA=B0=80=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../konkuk/thip/record/domain/Record.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/java/konkuk/thip/record/domain/Record.java b/src/main/java/konkuk/thip/record/domain/Record.java index 77454bb56..6c890e1ea 100644 --- a/src/main/java/konkuk/thip/record/domain/Record.java +++ b/src/main/java/konkuk/thip/record/domain/Record.java @@ -96,4 +96,21 @@ private void checkCommentCountNotUnderflow() { throw new InvalidStateException(COMMENT_COUNT_UNDERFLOW); } } -} + + private void validateCreator(Long userId) { + if (!this.creatorId.equals(userId)) { + throw new InvalidStateException(RECORD_ACCESS_FORBIDDEN, new IllegalArgumentException("기록 작성자만 기록을 수정/삭제할 수 있습니다.")); + } + } + + public void validateDeletable(Long userId) { + validateCreator(userId); + } + + public void validateRoomId(Long roomId) { + if (!this.roomId.equals(roomId)) { + throw new InvalidStateException(RECORD_ACCESS_FORBIDDEN, new IllegalArgumentException("기록이 해당 방에 속하지 않습니다.")); + } + } + +} \ No newline at end of file From b229535465ed8924991324f36a8f4f8eeadb5ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Fri, 8 Aug 2025 23:32:52 +0900 Subject: [PATCH 03/26] =?UTF-8?q?[feat]=20=EA=B8=B0=EB=A1=9D=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../in/web/RecordCommandController.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/main/java/konkuk/thip/record/adapter/in/web/RecordCommandController.java b/src/main/java/konkuk/thip/record/adapter/in/web/RecordCommandController.java index dfca9e812..e5778f96a 100644 --- a/src/main/java/konkuk/thip/record/adapter/in/web/RecordCommandController.java +++ b/src/main/java/konkuk/thip/record/adapter/in/web/RecordCommandController.java @@ -9,12 +9,12 @@ import konkuk.thip.common.swagger.annotation.ExceptionDescription; import konkuk.thip.record.adapter.in.web.request.RecordCreateRequest; import konkuk.thip.record.adapter.in.web.response.RecordCreateResponse; +import konkuk.thip.record.adapter.in.web.response.RecordDeleteResponse; import konkuk.thip.record.application.port.in.RecordCreateUseCase; +import konkuk.thip.record.application.port.in.RecordDeleteUseCase; +import konkuk.thip.record.application.port.in.dto.RecordDeleteCommand; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import static konkuk.thip.common.swagger.SwaggerResponseDescription.*; @@ -23,6 +23,7 @@ @RequiredArgsConstructor public class RecordCommandController { private final RecordCreateUseCase recordCreateUseCase; + private final RecordDeleteUseCase recordDeleteUseCase; @Operation( summary = "기록 생성", @@ -41,4 +42,17 @@ public BaseResponse createRecord( )); } + @Operation( + summary = "기록 삭제", + description = "사용자가 기록을 삭제합니다." + ) + @ExceptionDescription(RECORD_DELETE) + @DeleteMapping("/rooms/{roomId}/record/{recordId}") + public BaseResponse deleteRecord( + @Parameter(description = "삭제하려는 기록 ID", example = "1") @PathVariable("recordId") final Long recordId, + @Parameter(description = "삭제하려는 기록이 작성된 모임 ID", example = "1") @PathVariable("roomId") final Long roomId, + @Parameter(hidden = true) @UserId final Long userId) { + return BaseResponse.ok(RecordDeleteResponse.of(recordDeleteUseCase.deleteRecord(new RecordDeleteCommand(roomId, recordId, userId)))); + } + } From 3ef2cef7a62981fd77d67d1def3fb46b1b072bca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Fri, 8 Aug 2025 23:33:05 +0900 Subject: [PATCH 04/26] =?UTF-8?q?[feat]=20RecordCommandPersistenceAdapter.?= =?UTF-8?q?delete=20=EC=9E=91=EC=84=B1=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/RecordCommandPersistenceAdapter.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordCommandPersistenceAdapter.java b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordCommandPersistenceAdapter.java index d1170074c..146a576e9 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordCommandPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordCommandPersistenceAdapter.java @@ -15,6 +15,7 @@ import java.util.Optional; +import static konkuk.thip.common.entity.StatusType.ACTIVE; import static konkuk.thip.common.exception.code.ErrorCode.*; @Repository @@ -43,10 +44,19 @@ public Long saveRecord(Record record) { @Override public Optional findById(Long id) { - return recordJpaRepository.findById(id) + return recordJpaRepository.findByPostIdAndStatus(id, ACTIVE) .map(recordMapper::toDomainEntity); } + @Override + public void delete(Record record) { + RecordJpaEntity recordJpaEntity = recordJpaRepository.findById(record.getId()).orElseThrow( + () -> new EntityNotFoundException(RECORD_NOT_FOUND) + ); + + recordJpaRepository.delete(recordJpaEntity); + } + @Override public void update(Record record) { RecordJpaEntity recordJpaEntity = recordJpaRepository.findById(record.getId()).orElseThrow( From 59dcda07cecc7aa8e75cd6e360f5f95d776ce9a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Fri, 8 Aug 2025 23:33:25 +0900 Subject: [PATCH 05/26] =?UTF-8?q?[feat]=20RecordCommandPort.delete=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/record/application/port/out/RecordCommandPort.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/konkuk/thip/record/application/port/out/RecordCommandPort.java b/src/main/java/konkuk/thip/record/application/port/out/RecordCommandPort.java index a662daf86..5c23224b2 100644 --- a/src/main/java/konkuk/thip/record/application/port/out/RecordCommandPort.java +++ b/src/main/java/konkuk/thip/record/application/port/out/RecordCommandPort.java @@ -20,4 +20,6 @@ default Record getByIdOrThrow(Long id) { return findById(id) .orElseThrow(() -> new EntityNotFoundException(RECORD_NOT_FOUND)); } + + void delete(Record record); } From 084fd3180a0714db8262ce738d50e5b9a7542d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Fri, 8 Aug 2025 23:34:08 +0900 Subject: [PATCH 06/26] =?UTF-8?q?[feat]=20RecordDeleteCommand=20=20dto=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/port/in/dto/RecordDeleteCommand.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/main/java/konkuk/thip/record/application/port/in/dto/RecordDeleteCommand.java diff --git a/src/main/java/konkuk/thip/record/application/port/in/dto/RecordDeleteCommand.java b/src/main/java/konkuk/thip/record/application/port/in/dto/RecordDeleteCommand.java new file mode 100644 index 000000000..e0f9218af --- /dev/null +++ b/src/main/java/konkuk/thip/record/application/port/in/dto/RecordDeleteCommand.java @@ -0,0 +1,10 @@ +package konkuk.thip.record.application.port.in.dto; + +public record RecordDeleteCommand( + Long roomId, + + Long recordId, + + Long userId +) { +} From 83347f00d1f71dc8baac35f4edf0386256a6865a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Fri, 8 Aug 2025 23:34:15 +0900 Subject: [PATCH 07/26] =?UTF-8?q?[feat]=20RecordDeleteResponse=20dto=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/web/response/RecordDeleteResponse.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/konkuk/thip/record/adapter/in/web/response/RecordDeleteResponse.java diff --git a/src/main/java/konkuk/thip/record/adapter/in/web/response/RecordDeleteResponse.java b/src/main/java/konkuk/thip/record/adapter/in/web/response/RecordDeleteResponse.java new file mode 100644 index 000000000..b6bc76ca1 --- /dev/null +++ b/src/main/java/konkuk/thip/record/adapter/in/web/response/RecordDeleteResponse.java @@ -0,0 +1,7 @@ +package konkuk.thip.record.adapter.in.web.response; + +public record RecordDeleteResponse(Long roomId) { + public static RecordDeleteResponse of(Long roomId) { + return new RecordDeleteResponse(roomId); + } +} From 1d37ddc34a152315f84f18911ef164174bcad261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Fri, 8 Aug 2025 23:34:58 +0900 Subject: [PATCH 08/26] =?UTF-8?q?[feat]=20=EA=B8=B0=EB=A1=9D=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EC=9C=A0=EC=A6=88=EC=BC=80=EC=9D=B4=EC=8A=A4=20Rec?= =?UTF-8?q?ordDeleteUseCase=20=EC=9E=91=EC=84=B1=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../record/application/port/in/RecordDeleteUseCase.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/konkuk/thip/record/application/port/in/RecordDeleteUseCase.java diff --git a/src/main/java/konkuk/thip/record/application/port/in/RecordDeleteUseCase.java b/src/main/java/konkuk/thip/record/application/port/in/RecordDeleteUseCase.java new file mode 100644 index 000000000..208454c84 --- /dev/null +++ b/src/main/java/konkuk/thip/record/application/port/in/RecordDeleteUseCase.java @@ -0,0 +1,7 @@ +package konkuk.thip.record.application.port.in; + +import konkuk.thip.record.application.port.in.dto.RecordDeleteCommand; + +public interface RecordDeleteUseCase { + Long deleteRecord(RecordDeleteCommand command); +} From 11b150efd5848797d39ba0f2898d22ff3916ab36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Fri, 8 Aug 2025 23:35:08 +0900 Subject: [PATCH 09/26] =?UTF-8?q?[feat]=20=EA=B8=B0=EB=A1=9D=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EC=9C=A0=EC=A6=88=EC=BC=80=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=EC=B2=B4=20RecordDeleteService=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/RecordDeleteService.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/main/java/konkuk/thip/record/application/service/RecordDeleteService.java diff --git a/src/main/java/konkuk/thip/record/application/service/RecordDeleteService.java b/src/main/java/konkuk/thip/record/application/service/RecordDeleteService.java new file mode 100644 index 000000000..6b18b47f3 --- /dev/null +++ b/src/main/java/konkuk/thip/record/application/service/RecordDeleteService.java @@ -0,0 +1,37 @@ +package konkuk.thip.record.application.service; + +import jakarta.transaction.Transactional; +import konkuk.thip.record.application.port.in.RecordDeleteUseCase; +import konkuk.thip.record.application.port.in.dto.RecordDeleteCommand; +import konkuk.thip.record.application.port.out.RecordCommandPort; +import konkuk.thip.record.domain.Record; +import konkuk.thip.room.application.service.validator.RoomParticipantValidator; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class RecordDeleteService implements RecordDeleteUseCase { + + private final RecordCommandPort recordCommandPort; + private final RoomParticipantValidator roomParticipantValidator; + + @Override + @Transactional + public Long deleteRecord(RecordDeleteCommand command) { + + // 1. 방 참여자 검증 + roomParticipantValidator.validateUserIsRoomMember(command.roomId(), command.userId()); + + // 2. 기록 조회 및 검증 + Record record = recordCommandPort.getByIdOrThrow(command.recordId()); + // 2-1. 기록 삭제 권한 검증 + record.validateRoomId(command.roomId()); + record.validateDeletable(command.userId()); + + // 3. 기록 삭제 + recordCommandPort.delete(record); + + return command.roomId(); + } +} From daccf42f2e1edcabdabd45e8855f1345ba0447f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Fri, 8 Aug 2025 23:35:18 +0900 Subject: [PATCH 10/26] =?UTF-8?q?[feat]=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=9A=A9=20updateCommentCount=20=EC=B6=94=EA=B0=80=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../konkuk/thip/record/adapter/out/jpa/RecordJpaEntity.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/konkuk/thip/record/adapter/out/jpa/RecordJpaEntity.java b/src/main/java/konkuk/thip/record/adapter/out/jpa/RecordJpaEntity.java index 62acb2de9..d113c61ba 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/jpa/RecordJpaEntity.java +++ b/src/main/java/konkuk/thip/record/adapter/out/jpa/RecordJpaEntity.java @@ -52,5 +52,10 @@ public void updateLikeCount(int likeCount) { this.likeCount = likeCount; } + @VisibleForTesting + public void updateCommentCount(int commentCount) { + this.commentCount = commentCount; + } + } From 8fab215270bfdd0dcaab62a79f5b14e32205b483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Fri, 8 Aug 2025 23:35:32 +0900 Subject: [PATCH 11/26] =?UTF-8?q?[feat]=20RecordJpaRepository.findByPostId?= =?UTF-8?q?AndStatus=20=EC=9E=91=EC=84=B1=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../out/persistence/repository/RecordJpaRepository.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/konkuk/thip/record/adapter/out/persistence/repository/RecordJpaRepository.java b/src/main/java/konkuk/thip/record/adapter/out/persistence/repository/RecordJpaRepository.java index cb67058dc..a5058f7db 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/persistence/repository/RecordJpaRepository.java +++ b/src/main/java/konkuk/thip/record/adapter/out/persistence/repository/RecordJpaRepository.java @@ -1,8 +1,11 @@ package konkuk.thip.record.adapter.out.persistence.repository; +import konkuk.thip.common.entity.StatusType; import konkuk.thip.record.adapter.out.jpa.RecordJpaEntity; import org.springframework.data.jpa.repository.JpaRepository; -public interface RecordJpaRepository extends JpaRepository, RecordQueryRepository { +import java.util.Optional; +public interface RecordJpaRepository extends JpaRepository, RecordQueryRepository { + Optional findByPostIdAndStatus(Long postId, StatusType status); } From 322d9c3c388b2374614fb09ed94b8d8a658e8a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Fri, 8 Aug 2025 23:35:40 +0900 Subject: [PATCH 12/26] =?UTF-8?q?[feat]=20=EA=B4=80=EB=A0=A8=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=EC=BD=94=EB=93=9C=20=EC=8A=A4=EC=9B=A8=EA=B1=B0=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/common/swagger/SwaggerResponseDescription.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java b/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java index cea80d77a..1893e5970 100644 --- a/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java +++ b/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java @@ -131,6 +131,11 @@ public enum SwaggerResponseDescription { BOOK_NOT_FOUND, ROOM_ACCESS_FORBIDDEN ))), + RECORD_DELETE(new LinkedHashSet<>(Set.of( + ROOM_ACCESS_FORBIDDEN, + RECORD_NOT_FOUND, + RECORD_ACCESS_FORBIDDEN + ))), // Vote VOTE_CREATE(new LinkedHashSet<>(Set.of( From 74db07f61f9658fff7cc913062a81ac9cdf58a77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Fri, 8 Aug 2025 23:35:49 +0900 Subject: [PATCH 13/26] =?UTF-8?q?[feat]=20=EA=B8=B0=EB=A1=9D=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=ED=86=B5=ED=95=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1=20=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/web/RecordDeleteAPITest.java | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 src/test/java/konkuk/thip/record/adapter/in/web/RecordDeleteAPITest.java diff --git a/src/test/java/konkuk/thip/record/adapter/in/web/RecordDeleteAPITest.java b/src/test/java/konkuk/thip/record/adapter/in/web/RecordDeleteAPITest.java new file mode 100644 index 000000000..80fc3c6e0 --- /dev/null +++ b/src/test/java/konkuk/thip/record/adapter/in/web/RecordDeleteAPITest.java @@ -0,0 +1,121 @@ +package konkuk.thip.record.adapter.in.web; + +import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; +import konkuk.thip.book.adapter.out.persistence.repository.BookJpaRepository; +import konkuk.thip.comment.adapter.out.jpa.CommentJpaEntity; +import konkuk.thip.comment.adapter.out.persistence.repository.CommentJpaRepository; +import konkuk.thip.comment.adapter.out.persistence.repository.CommentLikeJpaRepository; +import konkuk.thip.common.util.TestEntityFactory; +import konkuk.thip.post.adapter.out.persistence.PostLikeJpaRepository; +import konkuk.thip.record.adapter.out.jpa.RecordJpaEntity; +import konkuk.thip.record.adapter.out.persistence.repository.RecordJpaRepository; +import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; +import konkuk.thip.room.adapter.out.jpa.RoomJpaEntity; +import konkuk.thip.room.adapter.out.jpa.RoomParticipantRole; +import konkuk.thip.room.adapter.out.persistence.repository.RoomJpaRepository; +import konkuk.thip.room.adapter.out.persistence.repository.category.CategoryJpaRepository; +import konkuk.thip.room.adapter.out.persistence.repository.roomparticipant.RoomParticipantJpaRepository; +import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; +import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; +import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; +import konkuk.thip.user.adapter.out.persistence.repository.alias.AliasJpaRepository; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +import static konkuk.thip.common.entity.StatusType.INACTIVE; +import static konkuk.thip.common.post.PostType.FEED; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@ActiveProfiles("test") +@AutoConfigureMockMvc(addFilters = false) +@DisplayName("[통합] 기록 삭제 api 통합 테스트") +class RecordDeleteAPITest { + + @Autowired + private MockMvc mockMvc; + + @Autowired private AliasJpaRepository aliasJpaRepository; + @Autowired private UserJpaRepository userJpaRepository; + @Autowired private CategoryJpaRepository categoryJpaRepository; + @Autowired private BookJpaRepository bookJpaRepository; + @Autowired private CommentJpaRepository commentJpaRepository; + @Autowired private CommentLikeJpaRepository commentLikeJpaRepository; + @Autowired private PostLikeJpaRepository postLikeJpaRepository; + @Autowired private RecordJpaRepository recordJpaRepository; + @Autowired private RoomJpaRepository roomJpaRepository; + @Autowired private RoomParticipantJpaRepository roomParticipantJpaRepository; + + private AliasJpaEntity alias; + private UserJpaEntity user; + private CategoryJpaEntity category; + private BookJpaEntity book; + private CommentJpaEntity comment; + private RecordJpaEntity record; + private RoomJpaEntity room; + + @BeforeEach + void setUp() { + alias = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias()); + user = userJpaRepository.save(TestEntityFactory.createUser(alias)); + category = categoryJpaRepository.save(TestEntityFactory.createLiteratureCategory(alias)); + book = bookJpaRepository.save(TestEntityFactory.createBookWithISBN("9788954682152")); + room = roomJpaRepository.save(TestEntityFactory.createRoom(book,category)); + record = recordJpaRepository.save(TestEntityFactory.createRecord(user,room)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room, user, RoomParticipantRole.HOST, 0.0)); + postLikeJpaRepository.save(TestEntityFactory.createPostLike(user,record)); + comment = commentJpaRepository.save(TestEntityFactory.createComment(record, user, FEED)); + commentLikeJpaRepository.save(TestEntityFactory.createCommentLike(comment,user)); + record.updateLikeCount(1); + record.updateCommentCount(1); + recordJpaRepository.save(record); + comment.updateLikeCount(1); + commentJpaRepository.save(comment); + } + + @AfterEach + @Transactional + void tearDown() { + postLikeJpaRepository.deleteAllInBatch(); + commentLikeJpaRepository.deleteAllInBatch(); + commentJpaRepository.deleteAllInBatch(); + recordJpaRepository.deleteAllInBatch(); + roomParticipantJpaRepository.deleteAllInBatch(); + roomJpaRepository.deleteAllInBatch(); + bookJpaRepository.deleteAllInBatch(); + userJpaRepository.deleteAllInBatch(); + categoryJpaRepository.deleteAllInBatch(); + aliasJpaRepository.deleteAllInBatch(); + } + + @Test + @DisplayName("기록을 삭제하면 [soft delete]되고, 연관된 댓글, 댓글 좋아요도 모두 삭제된다") + void deleteRecord_success() throws Exception { + + // when + mockMvc.perform(delete("/rooms/{roomId}/record/{recordId}", room.getRoomId(), record.getPostId()) + .requestAttr("userId", user.getUserId())) + .andExpect(status().isOk()); + + + // then: 1) 기록 soft delete (status=INACTIVE) + assertThat(recordJpaRepository.findByPostIdAndStatus(record.getPostId(), INACTIVE)).isPresent(); + + // 2) 댓글 삭제 soft delete + assertThat(commentJpaRepository.findByCommentIdAndStatus(comment.getCommentId(),INACTIVE)).isPresent(); + + // 3) 댓글 좋아요 삭제 + long commentLikeCountAfter = commentLikeJpaRepository.count(); + assertThat(commentLikeCountAfter).isEqualTo(0); + } +} From deac37efe09cb4854301c47346f52f12938ab81a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Fri, 8 Aug 2025 23:36:11 +0900 Subject: [PATCH 14/26] =?UTF-8?q?[feat]=20=EA=B8=B0=EB=A1=9D=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=EA=B4=80=EB=A0=A8=20record=20=EB=8B=A8=EC=9C=84?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../konkuk/thip/record/domain/RecordTest.java | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/src/test/java/konkuk/thip/record/domain/RecordTest.java b/src/test/java/konkuk/thip/record/domain/RecordTest.java index 4dba6a5b0..9286d486a 100644 --- a/src/test/java/konkuk/thip/record/domain/RecordTest.java +++ b/src/test/java/konkuk/thip/record/domain/RecordTest.java @@ -6,8 +6,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import static konkuk.thip.common.exception.code.ErrorCode.COMMENT_COUNT_UNDERFLOW; -import static konkuk.thip.common.exception.code.ErrorCode.POST_LIKE_COUNT_UNDERFLOW; +import static konkuk.thip.common.exception.code.ErrorCode.*; import static org.junit.jupiter.api.Assertions.*; @DisplayName("[단위] Record 도메인 테스트") @@ -21,6 +20,10 @@ void setUp() { } private final Long CREATOR_ID = 1L; + private final Long OTHER_USER_ID = 2L; + + private final Long ROOM_ID = 1L; + private final Long OTHER_ROOM_ID = 2L; private Record createWithCommentRecord() { return Record.builder() @@ -31,7 +34,7 @@ private Record createWithCommentRecord() { .isOverview(false) .likeCount(0) .commentCount(1) - .roomId(100L) + .roomId(ROOM_ID) .build(); } @@ -176,4 +179,41 @@ void updateLikeCount_likeFalse_underflow_throws() { assertEquals(POST_LIKE_COUNT_UNDERFLOW, ex.getErrorCode()); } + @Test + @DisplayName("validateDeletable: 작성자가 아닌 경우 기록을 삭제하려고 하면 InvalidStateException이 발생한다.") + void validateDeletable_byNonCreator_throws(){ + Record record = createWithCommentRecord(); + InvalidStateException ex = assertThrows(InvalidStateException.class, + () -> record.validateDeletable(OTHER_USER_ID)); + + assertEquals(RECORD_ACCESS_FORBIDDEN, ex.getErrorCode()); + } + + @Test + @DisplayName("validateDeletable: 피드의 작성자인 경우 피드를 삭제 할 수 있다.") + void validateDeletable_byCreator_Success(){ + Record record = createWithCommentRecord(); + assertDoesNotThrow(() -> record.validateDeletable(CREATOR_ID)); + } + + + @Test + @DisplayName("validateRoomId: 전달된 roomId가 일치하면 예외가 발생하지 않는다") + void validateRoomId_validRoom_noException() { + Record record = createWithCommentRecord(); + + assertDoesNotThrow(() -> record.validateRoomId(ROOM_ID)); + } + + @Test + @DisplayName("validateRoomId: 전달된 roomId가 다르면 InvalidStateException이 발생한다.") + void validateRoomId_differentRoom_throwsInvalidStateException() { + Record record = createWithCommentRecord(); + + InvalidStateException ex = assertThrows(InvalidStateException.class, + () -> record.validateRoomId(OTHER_ROOM_ID)); + + assertEquals(RECORD_ACCESS_FORBIDDEN, ex.getErrorCode()); + } + } \ No newline at end of file From d5db07ae4e07a6b5875340428619d05d8eca7769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Fri, 8 Aug 2025 23:36:26 +0900 Subject: [PATCH 15/26] =?UTF-8?q?[remove]=20=EB=8D=94=EB=AF=B8=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=82=AD=EC=A0=9C=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../record/application/port/in/dto/DummyQuery.java | 9 --------- .../record/application/port/in/dto/DummyResult.java | 10 ---------- 2 files changed, 19 deletions(-) delete mode 100644 src/main/java/konkuk/thip/record/application/port/in/dto/DummyQuery.java delete mode 100644 src/main/java/konkuk/thip/record/application/port/in/dto/DummyResult.java diff --git a/src/main/java/konkuk/thip/record/application/port/in/dto/DummyQuery.java b/src/main/java/konkuk/thip/record/application/port/in/dto/DummyQuery.java deleted file mode 100644 index 7d484a9f6..000000000 --- a/src/main/java/konkuk/thip/record/application/port/in/dto/DummyQuery.java +++ /dev/null @@ -1,9 +0,0 @@ -package konkuk.thip.record.application.port.in.dto; - -import lombok.Builder; -import lombok.Getter; - -@Builder -@Getter -public class DummyQuery { -} diff --git a/src/main/java/konkuk/thip/record/application/port/in/dto/DummyResult.java b/src/main/java/konkuk/thip/record/application/port/in/dto/DummyResult.java deleted file mode 100644 index 163e3648b..000000000 --- a/src/main/java/konkuk/thip/record/application/port/in/dto/DummyResult.java +++ /dev/null @@ -1,10 +0,0 @@ -package konkuk.thip.record.application.port.in.dto; - -import lombok.Builder; -import lombok.Getter; - -@Getter -@Builder -public class DummyResult { - -} From 2437805750ddfcc9dee02bf858eaddb1f5cdb317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sat, 9 Aug 2025 00:10:48 +0900 Subject: [PATCH 16/26] =?UTF-8?q?[test]=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/record/adapter/in/web/RecordDeleteAPITest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/konkuk/thip/record/adapter/in/web/RecordDeleteAPITest.java b/src/test/java/konkuk/thip/record/adapter/in/web/RecordDeleteAPITest.java index 80fc3c6e0..87882a5b2 100644 --- a/src/test/java/konkuk/thip/record/adapter/in/web/RecordDeleteAPITest.java +++ b/src/test/java/konkuk/thip/record/adapter/in/web/RecordDeleteAPITest.java @@ -31,7 +31,7 @@ import org.springframework.transaction.annotation.Transactional; import static konkuk.thip.common.entity.StatusType.INACTIVE; -import static konkuk.thip.common.post.PostType.FEED; +import static konkuk.thip.common.post.PostType.RECORD; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -74,7 +74,7 @@ void setUp() { record = recordJpaRepository.save(TestEntityFactory.createRecord(user,room)); roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room, user, RoomParticipantRole.HOST, 0.0)); postLikeJpaRepository.save(TestEntityFactory.createPostLike(user,record)); - comment = commentJpaRepository.save(TestEntityFactory.createComment(record, user, FEED)); + comment = commentJpaRepository.save(TestEntityFactory.createComment(record, user, RECORD)); commentLikeJpaRepository.save(TestEntityFactory.createCommentLike(comment,user)); record.updateLikeCount(1); record.updateCommentCount(1); From d5b677f8e99c65696401591594dd49fb145b4d5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 10 Aug 2025 18:01:42 +0900 Subject: [PATCH 17/26] =?UTF-8?q?[refactor]=20Update/delete=20=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=20=EC=8B=9C=20@Modifying(clearAutomatically=20=3D=20t?= =?UTF-8?q?rue,=20flushAutomatically=20=3D=20true)=20=EC=86=8D=EC=84=B1=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=EB=A1=9C=20=EC=98=81=EC=86=8D=EC=84=B1=201?= =?UTF-8?q?=EC=B0=A8=20=EC=BA=90=EC=8B=9C=20=EC=82=AD=EC=A0=9C(#160)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../out/persistence/repository/CommentLikeJpaRepository.java | 4 ++-- .../persistence/repository/FeedTag/FeedTagJpaRepository.java | 2 +- .../post/adapter/out/persistence/PostLikeJpaRepository.java | 2 +- .../out/persistence/repository/RecentSearchJpaRepository.java | 2 +- .../out/persistence/repository/SavedFeedJpaRepository.java | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/konkuk/thip/comment/adapter/out/persistence/repository/CommentLikeJpaRepository.java b/src/main/java/konkuk/thip/comment/adapter/out/persistence/repository/CommentLikeJpaRepository.java index a79283150..257401146 100644 --- a/src/main/java/konkuk/thip/comment/adapter/out/persistence/repository/CommentLikeJpaRepository.java +++ b/src/main/java/konkuk/thip/comment/adapter/out/persistence/repository/CommentLikeJpaRepository.java @@ -14,7 +14,7 @@ public interface CommentLikeJpaRepository extends JpaRepository findAllByUserId(@Param("userId") Long userId); - @Modifying + @Modifying(clearAutomatically = true, flushAutomatically = true) @Query("DELETE FROM CommentLikeJpaEntity cl WHERE cl.userJpaEntity.userId = :userId AND cl.commentJpaEntity.commentId = :commentId") void deleteByUserIdAndCommentId(@Param("userId") Long userId, @Param("commentId") Long commentId); @@ -23,7 +23,7 @@ public interface CommentLikeJpaRepository extends JpaRepository{ - @Modifying + @Modifying(clearAutomatically = true, flushAutomatically = true) @Query("DELETE FROM FeedTagJpaEntity ft WHERE ft.feedJpaEntity = :feedJpaEntity") void deleteAllByFeedJpaEntity(@Param("feedJpaEntity") FeedJpaEntity feedJpaEntity); diff --git a/src/main/java/konkuk/thip/post/adapter/out/persistence/PostLikeJpaRepository.java b/src/main/java/konkuk/thip/post/adapter/out/persistence/PostLikeJpaRepository.java index ce052cb6e..540c630e8 100644 --- a/src/main/java/konkuk/thip/post/adapter/out/persistence/PostLikeJpaRepository.java +++ b/src/main/java/konkuk/thip/post/adapter/out/persistence/PostLikeJpaRepository.java @@ -21,7 +21,7 @@ Set findPostIdsLikedByUser(@Param("postIds") Set postIds, "WHERE pl.userJpaEntity.userId = :userId AND pl.postJpaEntity.postId = :postId") boolean existsByUserIdAndPostId(@Param("userId") Long userId, @Param("postId") Long postId); - @Modifying + @Modifying(clearAutomatically = true, flushAutomatically = true) @Query("DELETE FROM PostLikeJpaEntity pl WHERE pl.userJpaEntity.userId = :userId AND pl.postJpaEntity.postId = :postId") void deleteByUserIdAndPostId(@Param("userId") Long userId, @Param("postId") Long postId); } diff --git a/src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/repository/RecentSearchJpaRepository.java b/src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/repository/RecentSearchJpaRepository.java index 71a707798..654b18972 100644 --- a/src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/repository/RecentSearchJpaRepository.java +++ b/src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/repository/RecentSearchJpaRepository.java @@ -9,7 +9,7 @@ @Repository public interface RecentSearchJpaRepository extends JpaRepository, RecentSearchQueryRepository { - @Modifying + @Modifying(clearAutomatically = true, flushAutomatically = true) @Query("UPDATE RecentSearchJpaEntity r SET r.modifiedAt = CURRENT_TIMESTAMP WHERE r.recentSearchId = :recentSearchId") void updateModifiedAt(@Param("recentSearchId") Long recentSearchId); } diff --git a/src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedFeedJpaRepository.java b/src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedFeedJpaRepository.java index e98baf1f3..04b80db23 100644 --- a/src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedFeedJpaRepository.java +++ b/src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedFeedJpaRepository.java @@ -10,7 +10,7 @@ import java.util.Set; public interface SavedFeedJpaRepository extends JpaRepository { - @Modifying + @Modifying(clearAutomatically = true, flushAutomatically = true) @Query(value = "DELETE FROM saved_feeds WHERE user_id = :userId AND post_id = :feedId", nativeQuery = true) void deleteByUserIdAndFeedId(@Param("userId") Long userId, @Param("feedId") Long feedId); From 60e9a0c31404fe4b786599589b40ca3f3fcf51f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 10 Aug 2025 18:02:01 +0900 Subject: [PATCH 18/26] =?UTF-8?q?[test]=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/konkuk/thip/feed/adapter/in/web/FeedDeleteAPITest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedDeleteAPITest.java b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedDeleteAPITest.java index 25f5503bc..9d8ce6d80 100644 --- a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedDeleteAPITest.java +++ b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedDeleteAPITest.java @@ -93,7 +93,6 @@ void setUp() { } @AfterEach - @Transactional void tearDown() { postLikeJpaRepository.deleteAllInBatch(); savedFeedJpaRepository.deleteAllInBatch(); From e854b84538557c1f9e746a24b7e3d39c78a9c889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 10 Aug 2025 18:07:26 +0900 Subject: [PATCH 19/26] =?UTF-8?q?[refactor]=20Update/delete=20=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=20=EC=8B=9C=20@Modifying(clearAutomatically=20=3D=20t?= =?UTF-8?q?rue,=20flushAutomatically=20=3D=20true)=20=EC=86=8D=EC=84=B1=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=EB=A1=9C=20=EC=98=81=EC=86=8D=EC=84=B1=201?= =?UTF-8?q?=EC=B0=A8=20=EC=BA=90=EC=8B=9C=20=EC=82=AD=EC=A0=9C(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../out/persistence/repository/SavedBookJpaRepository.java | 2 +- .../out/persistence/repository/CommentJpaRepository.java | 2 +- .../out/persistence/repository/CommentLikeJpaRepository.java | 2 +- .../persistence/repository/Content/ContentJpaRepository.java | 2 +- .../out/persistence/repository/SavedFeedJpaRepository.java | 2 +- .../post/adapter/out/persistence/PostLikeJpaRepository.java | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) 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 19202fb07..234b9bfd4 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 @@ -10,7 +10,7 @@ public interface SavedBookJpaRepository extends JpaRepository, CommentQueryRepository { Optional findByCommentIdAndStatus(Long commentId, StatusType status); - @Modifying + @Modifying(clearAutomatically = true, flushAutomatically = true) @Query("UPDATE CommentJpaEntity c SET c.status = 'INACTIVE' WHERE c.postJpaEntity.postId = :postId") void softDeleteAllByPostId(@Param("postId") Long postId); diff --git a/src/main/java/konkuk/thip/comment/adapter/out/persistence/repository/CommentLikeJpaRepository.java b/src/main/java/konkuk/thip/comment/adapter/out/persistence/repository/CommentLikeJpaRepository.java index d8db4b3a1..8e4d9bb35 100644 --- a/src/main/java/konkuk/thip/comment/adapter/out/persistence/repository/CommentLikeJpaRepository.java +++ b/src/main/java/konkuk/thip/comment/adapter/out/persistence/repository/CommentLikeJpaRepository.java @@ -31,7 +31,7 @@ public interface CommentLikeJpaRepository extends JpaRepository findCommentIdsLikedByUser(@Param("commentIds") Set commentIds, @Param("userId") Long userId); - @Modifying + @Modifying(clearAutomatically = true, flushAutomatically = true) @Query(""" DELETE FROM CommentLikeJpaEntity cl WHERE cl.commentJpaEntity.commentId IN ( diff --git a/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/Content/ContentJpaRepository.java b/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/Content/ContentJpaRepository.java index 6c98eba8f..ac9e08eee 100644 --- a/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/Content/ContentJpaRepository.java +++ b/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/Content/ContentJpaRepository.java @@ -8,7 +8,7 @@ public interface ContentJpaRepository extends JpaRepository{ - @Modifying + @Modifying(clearAutomatically = true, flushAutomatically = true) @Query("DELETE FROM ContentJpaEntity c WHERE c.postJpaEntity.postId = :feedId") void deleteAllByFeedId(@Param("feedId") Long feedId); } 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 6a2405c30..9e4824066 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 @@ -21,7 +21,7 @@ public interface SavedFeedJpaRepository extends JpaRepository findSavedFeedIdsByUserIdAndFeedIds(@Param("userId") Long userId, @Param("feedIds") Set feedIds); - @Modifying + @Modifying(clearAutomatically = true, flushAutomatically = true) @Query("DELETE FROM SavedFeedJpaEntity sf WHERE sf.feedJpaEntity.postId = :feedId") int deleteAllByFeedId(@Param("feedId") Long feedId); } diff --git a/src/main/java/konkuk/thip/post/adapter/out/persistence/PostLikeJpaRepository.java b/src/main/java/konkuk/thip/post/adapter/out/persistence/PostLikeJpaRepository.java index 19bf144ed..8b291e553 100644 --- a/src/main/java/konkuk/thip/post/adapter/out/persistence/PostLikeJpaRepository.java +++ b/src/main/java/konkuk/thip/post/adapter/out/persistence/PostLikeJpaRepository.java @@ -25,7 +25,7 @@ Set findPostIdsLikedByUser(@Param("postIds") Set postIds, @Query("DELETE FROM PostLikeJpaEntity pl WHERE pl.userJpaEntity.userId = :userId AND pl.postJpaEntity.postId = :postId") void deleteByUserIdAndPostId(@Param("userId") Long userId, @Param("postId") Long postId); - @Modifying + @Modifying(clearAutomatically = true, flushAutomatically = true) @Query("DELETE FROM PostLikeJpaEntity pl WHERE pl.postJpaEntity.postId = :postId") void deleteAllByPostId(@Param("postId") Long postId); } From a219ccd02a3f8927bd2d81f81ed060e52402cd4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 10 Aug 2025 18:19:16 +0900 Subject: [PATCH 20/26] =?UTF-8?q?[refactor]=20=EA=B8=B0=EB=A1=9D=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EB=B0=A9=20=EC=95=84=EC=9D=B4=EB=94=94=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EC=BA=A1=EC=8A=90=ED=99=94=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/konkuk/thip/record/domain/Record.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/konkuk/thip/record/domain/Record.java b/src/main/java/konkuk/thip/record/domain/Record.java index 6c890e1ea..8856140d9 100644 --- a/src/main/java/konkuk/thip/record/domain/Record.java +++ b/src/main/java/konkuk/thip/record/domain/Record.java @@ -103,11 +103,12 @@ private void validateCreator(Long userId) { } } - public void validateDeletable(Long userId) { + public void validateDeletable(Long userId,Long roomId) { + validateRoomId(roomId); validateCreator(userId); } - public void validateRoomId(Long roomId) { + private void validateRoomId(Long roomId) { if (!this.roomId.equals(roomId)) { throw new InvalidStateException(RECORD_ACCESS_FORBIDDEN, new IllegalArgumentException("기록이 해당 방에 속하지 않습니다.")); } From d369a33543f936a9ddea9d5ba4e74cb48b9d629d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 10 Aug 2025 18:19:33 +0900 Subject: [PATCH 21/26] =?UTF-8?q?[refactor]=20=EA=B8=B0=EB=A1=9D=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EC=86=8C=ED=94=84=ED=8A=B8=20=EB=94=9C?= =?UTF-8?q?=EB=A6=AC=ED=8A=B8=20=EC=A0=84=EB=9E=B5=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RecordCommandPersistenceAdapter.java | 3 ++- .../application/service/RecordDeleteService.java | 13 +++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordCommandPersistenceAdapter.java b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordCommandPersistenceAdapter.java index 146a576e9..de74af625 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordCommandPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordCommandPersistenceAdapter.java @@ -54,7 +54,8 @@ public void delete(Record record) { () -> new EntityNotFoundException(RECORD_NOT_FOUND) ); - recordJpaRepository.delete(recordJpaEntity); + recordJpaEntity.softDelete(); + recordJpaRepository.save(recordJpaEntity); } @Override diff --git a/src/main/java/konkuk/thip/record/application/service/RecordDeleteService.java b/src/main/java/konkuk/thip/record/application/service/RecordDeleteService.java index 6b18b47f3..aacd50f87 100644 --- a/src/main/java/konkuk/thip/record/application/service/RecordDeleteService.java +++ b/src/main/java/konkuk/thip/record/application/service/RecordDeleteService.java @@ -1,6 +1,8 @@ package konkuk.thip.record.application.service; import jakarta.transaction.Transactional; +import konkuk.thip.comment.application.port.out.CommentCommandPort; +import konkuk.thip.post.application.port.out.PostLikeCommandPort; import konkuk.thip.record.application.port.in.RecordDeleteUseCase; import konkuk.thip.record.application.port.in.dto.RecordDeleteCommand; import konkuk.thip.record.application.port.out.RecordCommandPort; @@ -14,6 +16,9 @@ public class RecordDeleteService implements RecordDeleteUseCase { private final RecordCommandPort recordCommandPort; + private final CommentCommandPort commentCommandPort; + private final PostLikeCommandPort postLikeCommandPort; + private final RoomParticipantValidator roomParticipantValidator; @Override @@ -26,10 +31,14 @@ public Long deleteRecord(RecordDeleteCommand command) { // 2. 기록 조회 및 검증 Record record = recordCommandPort.getByIdOrThrow(command.recordId()); // 2-1. 기록 삭제 권한 검증 - record.validateRoomId(command.roomId()); - record.validateDeletable(command.userId()); + record.validateDeletable(command.userId(),command.roomId()); // 3. 기록 삭제 + // 3-1. 기록 게시물 댓글 삭제 + commentCommandPort.softDeleteAllByPostId(command.recordId()); + // 3-2. 피드 게시물 좋아요 삭제 + postLikeCommandPort.deleteAllByPostId(command.recordId()); + // 3-3. 기록 삭제 recordCommandPort.delete(record); return command.roomId(); From f4fe287ce48f6b195e829eb154c7d5d1a9377c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 10 Aug 2025 18:20:18 +0900 Subject: [PATCH 22/26] =?UTF-8?q?[chore]=20=ED=94=BC=EB=93=9C=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B4=80=EB=A0=A8=20=EC=A3=BC=EC=84=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/feed/application/service/FeedDeleteService.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/konkuk/thip/feed/application/service/FeedDeleteService.java b/src/main/java/konkuk/thip/feed/application/service/FeedDeleteService.java index 1fb28d0bb..ef0e94f6f 100644 --- a/src/main/java/konkuk/thip/feed/application/service/FeedDeleteService.java +++ b/src/main/java/konkuk/thip/feed/application/service/FeedDeleteService.java @@ -29,8 +29,11 @@ public void deleteFeed(Long feedId, Long userId) { // TODO S3 이미지 삭제 이벤트 기반 처리 or 배치 삭제 // 3. 피드 삭제 + // 3-1. 피드 게시물 댓글 삭제 commentCommandPort.softDeleteAllByPostId(feedId); + // 3-2. 피드 게시물 좋아요 삭제 postLikeCommandPort.deleteAllByPostId(feedId); + // 3-3. 피드 삭제 및 관련 엔티티(피드_태그, 콘텐츠, 피드 저장) 삭제 feedCommandPort.delete(feed); } } From 2519adf26ce5dc5a98df215858b8a01cea722065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 10 Aug 2025 18:20:38 +0900 Subject: [PATCH 23/26] =?UTF-8?q?[test]=20=ED=94=BC=EB=93=9C=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/adapter/in/web/FeedDeleteAPITest.java | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedDeleteAPITest.java b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedDeleteAPITest.java index a9a38c95e..22d75ecdb 100644 --- a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedDeleteAPITest.java +++ b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedDeleteAPITest.java @@ -20,7 +20,6 @@ import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; import konkuk.thip.user.adapter.out.persistence.repository.alias.AliasJpaRepository; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -43,6 +42,7 @@ @SpringBootTest @ActiveProfiles("test") +@Transactional @AutoConfigureMockMvc(addFilters = false) @DisplayName("[통합] 피드 삭제 api 통합 테스트") class FeedDeleteAPITest { @@ -92,21 +92,6 @@ void setUp() { commentJpaRepository.save(comment); } - @AfterEach - void tearDown() { - postLikeJpaRepository.deleteAllInBatch(); - savedFeedJpaRepository.deleteAllInBatch(); - commentLikeJpaRepository.deleteAllInBatch(); - commentJpaRepository.deleteAllInBatch(); - contentJpaRepository.deleteAllInBatch(); - feedTagJpaRepository.deleteAllInBatch(); - feedJpaRepository.deleteAllInBatch(); - bookJpaRepository.deleteAllInBatch(); - userJpaRepository.deleteAllInBatch(); - tagJpaRepository.deleteAllInBatch(); - categoryJpaRepository.deleteAllInBatch(); - aliasJpaRepository.deleteAllInBatch(); - } @Test @DisplayName("피드를 삭제하면 [soft delete]되고, 연관된 피드 태그 연관관계, 콘텐츠(사진), 댓글, 댓글 좋아요, 피드 저장관계도 모두 삭제된다") From 53d893058f0401298c1afe5e18bba16fe565f927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 10 Aug 2025 18:20:44 +0900 Subject: [PATCH 24/26] =?UTF-8?q?[test]=20=EA=B8=B0=EB=A1=9D=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/web/RecordDeleteAPITest.java | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/test/java/konkuk/thip/record/adapter/in/web/RecordDeleteAPITest.java b/src/test/java/konkuk/thip/record/adapter/in/web/RecordDeleteAPITest.java index 87882a5b2..fe78c1564 100644 --- a/src/test/java/konkuk/thip/record/adapter/in/web/RecordDeleteAPITest.java +++ b/src/test/java/konkuk/thip/record/adapter/in/web/RecordDeleteAPITest.java @@ -38,6 +38,7 @@ @SpringBootTest @ActiveProfiles("test") +@Transactional @AutoConfigureMockMvc(addFilters = false) @DisplayName("[통합] 기록 삭제 api 통합 테스트") class RecordDeleteAPITest { @@ -83,21 +84,6 @@ record = recordJpaRepository.save(TestEntityFactory.createRecord(user,room)); commentJpaRepository.save(comment); } - @AfterEach - @Transactional - void tearDown() { - postLikeJpaRepository.deleteAllInBatch(); - commentLikeJpaRepository.deleteAllInBatch(); - commentJpaRepository.deleteAllInBatch(); - recordJpaRepository.deleteAllInBatch(); - roomParticipantJpaRepository.deleteAllInBatch(); - roomJpaRepository.deleteAllInBatch(); - bookJpaRepository.deleteAllInBatch(); - userJpaRepository.deleteAllInBatch(); - categoryJpaRepository.deleteAllInBatch(); - aliasJpaRepository.deleteAllInBatch(); - } - @Test @DisplayName("기록을 삭제하면 [soft delete]되고, 연관된 댓글, 댓글 좋아요도 모두 삭제된다") void deleteRecord_success() throws Exception { From 72c85b72ec9a64a764f1261d8a1d74d0fc36b8f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 10 Aug 2025 18:20:52 +0900 Subject: [PATCH 25/26] =?UTF-8?q?[test]=20=EA=B8=B0=EB=A1=9D=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EB=8B=A8=EC=9C=84=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../konkuk/thip/record/domain/RecordTest.java | 30 +++++++------------ 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/test/java/konkuk/thip/record/domain/RecordTest.java b/src/test/java/konkuk/thip/record/domain/RecordTest.java index 9286d486a..a8fefdd5f 100644 --- a/src/test/java/konkuk/thip/record/domain/RecordTest.java +++ b/src/test/java/konkuk/thip/record/domain/RecordTest.java @@ -184,36 +184,26 @@ void updateLikeCount_likeFalse_underflow_throws() { void validateDeletable_byNonCreator_throws(){ Record record = createWithCommentRecord(); InvalidStateException ex = assertThrows(InvalidStateException.class, - () -> record.validateDeletable(OTHER_USER_ID)); + () -> record.validateDeletable(OTHER_USER_ID,ROOM_ID)); assertEquals(RECORD_ACCESS_FORBIDDEN, ex.getErrorCode()); } @Test - @DisplayName("validateDeletable: 피드의 작성자인 경우 피드를 삭제 할 수 있다.") - void validateDeletable_byCreator_Success(){ + @DisplayName("validateDeletable: 전달된 roomId가 기록의 roomId가 일치 하지않은 경우 기록을 삭제하려고 하면 InvalidStateException이 발생한다.") + void validateDeletable_byOtherRoomId_throws(){ Record record = createWithCommentRecord(); - assertDoesNotThrow(() -> record.validateDeletable(CREATOR_ID)); - } - - - @Test - @DisplayName("validateRoomId: 전달된 roomId가 일치하면 예외가 발생하지 않는다") - void validateRoomId_validRoom_noException() { - Record record = createWithCommentRecord(); + InvalidStateException ex = assertThrows(InvalidStateException.class, + () -> record.validateDeletable(CREATOR_ID,OTHER_ROOM_ID)); - assertDoesNotThrow(() -> record.validateRoomId(ROOM_ID)); + assertEquals(RECORD_ACCESS_FORBIDDEN, ex.getErrorCode()); } @Test - @DisplayName("validateRoomId: 전달된 roomId가 다르면 InvalidStateException이 발생한다.") - void validateRoomId_differentRoom_throwsInvalidStateException() { - Record record = createWithCommentRecord(); - - InvalidStateException ex = assertThrows(InvalidStateException.class, - () -> record.validateRoomId(OTHER_ROOM_ID)); - - assertEquals(RECORD_ACCESS_FORBIDDEN, ex.getErrorCode()); + @DisplayName("validateDeletable: 피드의 작성자면서, 전달된 roomId가 기록의 roomId와 일치할 경우 기록을 삭제 할 수 있다.") + void validateDeletable_byCreator_byRoomId_Success(){ + Record record = createWithCommentRecord(); + assertDoesNotThrow(() -> record.validateDeletable(CREATOR_ID,ROOM_ID)); } } \ No newline at end of file From 187a2e4ccb33593d7ed24143e077de5a468e9fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Mon, 11 Aug 2025 14:15:39 +0900 Subject: [PATCH 26/26] =?UTF-8?q?[teat]=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20(#160)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../konkuk/thip/feed/adapter/in/web/FeedDeleteAPITest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedDeleteAPITest.java b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedDeleteAPITest.java index 22d75ecdb..6dec59d60 100644 --- a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedDeleteAPITest.java +++ b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedDeleteAPITest.java @@ -34,7 +34,8 @@ import static konkuk.thip.common.entity.StatusType.INACTIVE; import static konkuk.thip.common.post.PostType.FEED; -import static konkuk.thip.feed.domain.Tag.*; +import static konkuk.thip.feed.domain.Tag.FOREIGN_NOVEL; +import static konkuk.thip.feed.domain.Tag.KOREAN_NOVEL; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; @@ -79,8 +80,8 @@ void setUp() { user = userJpaRepository.save(TestEntityFactory.createUser(alias)); category = categoryJpaRepository.save(TestEntityFactory.createLiteratureCategory(alias)); book = bookJpaRepository.save(TestEntityFactory.createBookWithISBN("9788954682152")); - tag1 = tagJpaRepository.save(TestEntityFactory.createTag(category, BOOK_RECOMMEND.getValue())); - tag2 = tagJpaRepository.save(TestEntityFactory.createTag(category, TODAY_BOOK.getValue())); + tag1 = tagJpaRepository.save(TestEntityFactory.createTag(category, KOREAN_NOVEL.getValue())); + tag2 = tagJpaRepository.save(TestEntityFactory.createTag(category, FOREIGN_NOVEL.getValue())); feed = feedJpaRepository.save(TestEntityFactory.createFeed(user, book, true,1,1,List.of("url1", "url2", "url3"))); feedTagJpaRepository.save(TestEntityFactory.createFeedTagMapping(feed, tag1)); feedTagJpaRepository.save(TestEntityFactory.createFeedTagMapping(feed, tag2));