From 301fdd6ecd02c11f6837020569a61b14758e3742 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Fri, 11 Jul 2025 16:34:06 +0900 Subject: [PATCH 01/31] =?UTF-8?q?[refactor]=20Feed,=20Record,=20Vote?= =?UTF-8?q?=EC=97=90=20likeCount,=20commentCount=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/adapter/out/jpa/FeedJpaEntity.java | 4 +-- .../feed/adapter/out/mapper/FeedMapper.java | 4 +++ .../java/konkuk/thip/feed/domain/Feed.java | 25 ++++++++++++++++++- .../post/adapter/out/jpa/PostJpaEntity.java | 8 +++++- .../adapter/out/jpa/RecordJpaEntity.java | 4 +-- .../adapter/out/mapper/RecordMapper.java | 4 +++ .../konkuk/thip/record/domain/Record.java | 6 +++++ .../vote/adapter/out/jpa/VoteJpaEntity.java | 4 +-- .../vote/adapter/out/mapper/VoteMapper.java | 4 +++ .../java/konkuk/thip/vote/domain/Vote.java | 6 +++++ 10 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/main/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntity.java b/src/main/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntity.java index 34aa496aa..2dcd983d1 100644 --- a/src/main/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntity.java +++ b/src/main/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntity.java @@ -28,8 +28,8 @@ public class FeedJpaEntity extends PostJpaEntity { private BookJpaEntity bookJpaEntity; @Builder - public FeedJpaEntity(String content, UserJpaEntity userJpaEntity, Boolean isPublic, int reportCount, BookJpaEntity bookJpaEntity) { - super(content, userJpaEntity); + public FeedJpaEntity(String content, Integer likeCount, Integer commentCount, UserJpaEntity userJpaEntity, Boolean isPublic, int reportCount, BookJpaEntity bookJpaEntity) { + super(content, likeCount, commentCount, userJpaEntity); this.isPublic = isPublic; this.reportCount = reportCount; this.bookJpaEntity = bookJpaEntity; diff --git a/src/main/java/konkuk/thip/feed/adapter/out/mapper/FeedMapper.java b/src/main/java/konkuk/thip/feed/adapter/out/mapper/FeedMapper.java index 4566dc332..c3600919c 100644 --- a/src/main/java/konkuk/thip/feed/adapter/out/mapper/FeedMapper.java +++ b/src/main/java/konkuk/thip/feed/adapter/out/mapper/FeedMapper.java @@ -15,6 +15,8 @@ public FeedJpaEntity toJpaEntity(Feed feed, UserJpaEntity userJpaEntity, BookJpa .userJpaEntity(userJpaEntity) .isPublic(feed.getIsPublic()) .reportCount(feed.getReportCount()) + .likeCount(feed.getLikeCount()) + .commentCount(feed.getCommentCount()) .bookJpaEntity(bookJpaEntity) .build(); } @@ -26,6 +28,8 @@ public Feed toDomainEntity(FeedJpaEntity feedJpaEntity) { .creatorId(feedJpaEntity.getUserJpaEntity().getUserId()) .isPublic(feedJpaEntity.getIsPublic()) .reportCount(feedJpaEntity.getReportCount()) + .likeCount(feedJpaEntity.getLikeCount()) + .commentCount(feedJpaEntity.getCommentCount()) .targetBookId(feedJpaEntity.getBookJpaEntity().getBookId()) .createdAt(feedJpaEntity.getCreatedAt()) .modifiedAt(feedJpaEntity.getModifiedAt()) diff --git a/src/main/java/konkuk/thip/feed/domain/Feed.java b/src/main/java/konkuk/thip/feed/domain/Feed.java index 50ca5414a..c02231641 100644 --- a/src/main/java/konkuk/thip/feed/domain/Feed.java +++ b/src/main/java/konkuk/thip/feed/domain/Feed.java @@ -16,8 +16,31 @@ public class Feed extends BaseDomainEntity { private Boolean isPublic; - private int reportCount; + private Integer reportCount; + + private Integer likeCount = 0; + + private Integer commentCount = 0; private Long targetBookId; + public static Feed withoutId( + String content, + Long creatorId, + Boolean isPublic, + Integer reportCount, + Long targetBookId + ) { + return Feed.builder() + .id(null) + .content(content) + .creatorId(creatorId) + .isPublic(isPublic) + .reportCount(reportCount) + .targetBookId(targetBookId) + .likeCount(0) + .commentCount(0) + .build(); + } + } diff --git a/src/main/java/konkuk/thip/post/adapter/out/jpa/PostJpaEntity.java b/src/main/java/konkuk/thip/post/adapter/out/jpa/PostJpaEntity.java index c23cacd06..35fce5ada 100644 --- a/src/main/java/konkuk/thip/post/adapter/out/jpa/PostJpaEntity.java +++ b/src/main/java/konkuk/thip/post/adapter/out/jpa/PostJpaEntity.java @@ -23,12 +23,18 @@ public abstract class PostJpaEntity extends BaseJpaEntity { @Column(length = 6100, nullable = false) private String content; + private Integer likeCount = 0; + + private Integer commentCount = 0; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) private UserJpaEntity userJpaEntity; - public PostJpaEntity(String content, UserJpaEntity userJpaEntity) { + public PostJpaEntity(String content, Integer likeCount, Integer commentCount, UserJpaEntity userJpaEntity) { this.content = content; + this.likeCount = likeCount; + this.commentCount = commentCount; this.userJpaEntity = userJpaEntity; } } \ No newline at end of file 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 5ad164b39..b18654d09 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 @@ -26,8 +26,8 @@ public class RecordJpaEntity extends PostJpaEntity { private RoomJpaEntity roomJpaEntity; @Builder - public RecordJpaEntity(String content, UserJpaEntity userJpaEntity, Integer page, boolean isOverview, RoomJpaEntity roomJpaEntity) { - super(content, userJpaEntity); + public RecordJpaEntity(String content, Integer likeCount, Integer commentCount, UserJpaEntity userJpaEntity, Integer page, boolean isOverview, RoomJpaEntity roomJpaEntity) { + super(content, likeCount, commentCount, userJpaEntity); this.page = page; this.isOverview = isOverview; this.roomJpaEntity = roomJpaEntity; diff --git a/src/main/java/konkuk/thip/record/adapter/out/mapper/RecordMapper.java b/src/main/java/konkuk/thip/record/adapter/out/mapper/RecordMapper.java index 657095717..6997190e6 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/mapper/RecordMapper.java +++ b/src/main/java/konkuk/thip/record/adapter/out/mapper/RecordMapper.java @@ -12,6 +12,8 @@ public class RecordMapper { public RecordJpaEntity toJpaEntity(Record record, UserJpaEntity userJpaEntity, RoomJpaEntity roomJpaEntity) { return RecordJpaEntity.builder() .content(record.getContent()) + .likeCount(record.getLikeCount()) + .commentCount(record.getCommentCount()) .userJpaEntity(userJpaEntity) .page(record.getPage()) .isOverview(record.isOverview()) @@ -27,6 +29,8 @@ public Record toDomainEntity(RecordJpaEntity recordJpaEntity) { .page(recordJpaEntity.getPage()) .isOverview(recordJpaEntity.isOverview()) .roomId(recordJpaEntity.getRoomJpaEntity().getRoomId()) + .likeCount(recordJpaEntity.getLikeCount()) + .commentCount(recordJpaEntity.getCommentCount()) .createdAt(recordJpaEntity.getCreatedAt()) .modifiedAt(recordJpaEntity.getModifiedAt()) .status(recordJpaEntity.getStatus()) diff --git a/src/main/java/konkuk/thip/record/domain/Record.java b/src/main/java/konkuk/thip/record/domain/Record.java index ae46fa255..b989ec2fc 100644 --- a/src/main/java/konkuk/thip/record/domain/Record.java +++ b/src/main/java/konkuk/thip/record/domain/Record.java @@ -21,6 +21,10 @@ public class Record extends BaseDomainEntity { private boolean isOverview; + private Integer likeCount = 0; + + private Integer commentCount = 0; + private Long roomId; public static Record withoutId( @@ -35,6 +39,8 @@ public static Record withoutId( .creatorId(creatorId) .page(page) .isOverview(isOverview) + .likeCount(0) + .commentCount(0) .roomId(roomId) .build(); } diff --git a/src/main/java/konkuk/thip/vote/adapter/out/jpa/VoteJpaEntity.java b/src/main/java/konkuk/thip/vote/adapter/out/jpa/VoteJpaEntity.java index e25662e7c..d109c2eee 100644 --- a/src/main/java/konkuk/thip/vote/adapter/out/jpa/VoteJpaEntity.java +++ b/src/main/java/konkuk/thip/vote/adapter/out/jpa/VoteJpaEntity.java @@ -26,8 +26,8 @@ public class VoteJpaEntity extends PostJpaEntity { private RoomJpaEntity roomJpaEntity; @Builder - public VoteJpaEntity(String content, UserJpaEntity userJpaEntity, Integer page, boolean isOverview, RoomJpaEntity roomJpaEntity) { - super(content, userJpaEntity); + public VoteJpaEntity(String content, Integer likeCount, Integer commentCount, UserJpaEntity userJpaEntity, Integer page, boolean isOverview, RoomJpaEntity roomJpaEntity) { + super(content, likeCount, commentCount, userJpaEntity); this.page = page; this.isOverview = isOverview; this.roomJpaEntity = roomJpaEntity; diff --git a/src/main/java/konkuk/thip/vote/adapter/out/mapper/VoteMapper.java b/src/main/java/konkuk/thip/vote/adapter/out/mapper/VoteMapper.java index a4adb6d3f..d7ea120c4 100644 --- a/src/main/java/konkuk/thip/vote/adapter/out/mapper/VoteMapper.java +++ b/src/main/java/konkuk/thip/vote/adapter/out/mapper/VoteMapper.java @@ -15,6 +15,8 @@ public VoteJpaEntity toJpaEntity(Vote vote, UserJpaEntity userJpaEntity, RoomJpa .userJpaEntity(userJpaEntity) .page(vote.getPage()) .isOverview(vote.isOverview()) + .likeCount(vote.getLikeCount()) + .commentCount(vote.getCommentCount()) .roomJpaEntity(roomJpaEntity) .build(); } @@ -26,6 +28,8 @@ public Vote toDomainEntity(VoteJpaEntity voteJpaEntity) { .creatorId(voteJpaEntity.getUserJpaEntity().getUserId()) .page(voteJpaEntity.getPage()) .isOverview(voteJpaEntity.isOverview()) + .likeCount(voteJpaEntity.getLikeCount()) + .commentCount(voteJpaEntity.getCommentCount()) .roomId(voteJpaEntity.getRoomJpaEntity().getRoomId()) .createdAt(voteJpaEntity.getCreatedAt()) .modifiedAt(voteJpaEntity.getModifiedAt()) diff --git a/src/main/java/konkuk/thip/vote/domain/Vote.java b/src/main/java/konkuk/thip/vote/domain/Vote.java index 6cde37e24..e9540632a 100644 --- a/src/main/java/konkuk/thip/vote/domain/Vote.java +++ b/src/main/java/konkuk/thip/vote/domain/Vote.java @@ -21,6 +21,10 @@ public class Vote extends BaseDomainEntity { private boolean isOverview; + private Integer likeCount = 0; + + private Integer commentCount = 0; + private Long roomId; public static Vote withoutId(String content, Long creatorId, Integer page, boolean isOverview, Long roomId) { @@ -30,6 +34,8 @@ public static Vote withoutId(String content, Long creatorId, Integer page, boole .creatorId(creatorId) .page(page) .isOverview(isOverview) + .likeCount(0) + .commentCount(0) .roomId(roomId) .build(); } From f8daee60b4324d2225b239524db71d5677c0051a Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Fri, 11 Jul 2025 16:34:50 +0900 Subject: [PATCH 02/31] =?UTF-8?q?[refactor]=20DateUtil=20=EC=A0=95?= =?UTF-8?q?=EC=A0=81=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/konkuk/thip/common/util/DateUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/konkuk/thip/common/util/DateUtil.java b/src/main/java/konkuk/thip/common/util/DateUtil.java index 872c861e1..9b97f6a97 100644 --- a/src/main/java/konkuk/thip/common/util/DateUtil.java +++ b/src/main/java/konkuk/thip/common/util/DateUtil.java @@ -10,7 +10,7 @@ public class DateUtil { //마지막 활동 시간 포맷팅 -> ex. 1분 전, 1시간 전, 1일 전 - public String formatLastActivityTime(LocalDateTime createdAt) { + public static String formatLastActivityTime(LocalDateTime createdAt) { long minutes = Duration.between(createdAt, LocalDateTime.now()).toMinutes(); if (minutes < 1) return "방금 전"; if (minutes < 60) return minutes + "분 전"; From 183f5fdca89ee82969dfa4ab988067ec7b3d0d4f Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 00:44:55 +0900 Subject: [PATCH 03/31] =?UTF-8?q?[refactor]=20isOverview=20=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/record/adapter/in/web/RecordQueryController.java | 6 ++++-- .../record/application/port/in/dto/RecordSearchQuery.java | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/konkuk/thip/record/adapter/in/web/RecordQueryController.java b/src/main/java/konkuk/thip/record/adapter/in/web/RecordQueryController.java index 201413f12..e86c74b54 100644 --- a/src/main/java/konkuk/thip/record/adapter/in/web/RecordQueryController.java +++ b/src/main/java/konkuk/thip/record/adapter/in/web/RecordQueryController.java @@ -20,10 +20,11 @@ public class RecordQueryController { @GetMapping("/rooms/{roomId}/posts") public BaseResponse viewRecordList( @PathVariable final Long roomId, - @RequestParam final String type, - @RequestParam final String sort, + @RequestParam(required = false) final String type, + @RequestParam(required = false) final String sort, @RequestParam(required = false) final Integer pageStart, @RequestParam(required = false) final Integer pageEnd, + @RequestParam final Boolean isOverview, @RequestParam final Integer pageNum, @UserId final Long userId ) { @@ -34,6 +35,7 @@ public BaseResponse viewRecordList( .sort(sort) .pageStart(pageStart) .pageEnd(pageEnd) + .isOverview(isOverview) .pageNum(pageNum) .userId(userId) .build() diff --git a/src/main/java/konkuk/thip/record/application/port/in/dto/RecordSearchQuery.java b/src/main/java/konkuk/thip/record/application/port/in/dto/RecordSearchQuery.java index a32a45f90..c35f79ca6 100644 --- a/src/main/java/konkuk/thip/record/application/port/in/dto/RecordSearchQuery.java +++ b/src/main/java/konkuk/thip/record/application/port/in/dto/RecordSearchQuery.java @@ -9,6 +9,7 @@ public record RecordSearchQuery( String sort, Integer pageStart, Integer pageEnd, + Boolean isOverview, Integer pageNum, Long userId) { } From 565cd55d821f4bf3043852a7fa79c952424fa10a Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 02:42:26 +0900 Subject: [PATCH 04/31] =?UTF-8?q?[refactor]=20dto=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=ED=95=84=EC=9A=94=ED=95=9C=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80=20(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/web/response/RecordDto.java | 18 +++++++++++++++++- .../in/web/response/RecordSearchResponse.java | 6 +++--- .../adapter/in/web/response/VoteDto.java | 19 ++++++++++++++++++- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/main/java/konkuk/thip/record/adapter/in/web/response/RecordDto.java b/src/main/java/konkuk/thip/record/adapter/in/web/response/RecordDto.java index 0fda7c5fc..fa218ea7a 100644 --- a/src/main/java/konkuk/thip/record/adapter/in/web/response/RecordDto.java +++ b/src/main/java/konkuk/thip/record/adapter/in/web/response/RecordDto.java @@ -17,7 +17,7 @@ public record RecordDto( boolean isLiked, boolean isWriter, Long recordId -) implements RecordSearchResponse.PostDto { +) implements RecordSearchResponse.RecordSearchResult { @Override public String type() { return "RECORD"; @@ -38,4 +38,20 @@ public static RecordDto of(Record record, String postDate, User user, int likeCo .recordId(record.getId()) .build(); } + + public RecordDto withIsLiked(boolean isLiked) { + return new RecordDto( + postDate, + page, + userId, + nickName, + profileImageUrl, + content, + likeCount, + commentCount, + isLiked, + isWriter, + recordId + ); + } } diff --git a/src/main/java/konkuk/thip/record/adapter/in/web/response/RecordSearchResponse.java b/src/main/java/konkuk/thip/record/adapter/in/web/response/RecordSearchResponse.java index 0a68a857b..46f0edd21 100644 --- a/src/main/java/konkuk/thip/record/adapter/in/web/response/RecordSearchResponse.java +++ b/src/main/java/konkuk/thip/record/adapter/in/web/response/RecordSearchResponse.java @@ -6,14 +6,14 @@ import java.util.List; public record RecordSearchResponse( - List recordList, + List recordList, Integer page, Integer size, Boolean first, Boolean last ){ - public static RecordSearchResponse of(List recordList, + public static RecordSearchResponse of(List recordList, Integer page, Integer size, Boolean first, @@ -26,7 +26,7 @@ public static RecordSearchResponse of(List recordList, @JsonSubTypes.Type(value = RecordDto.class, name = "RECORD"), @JsonSubTypes.Type(value = VoteDto.class, name = "VOTE") }) - public sealed interface PostDto permits RecordDto, VoteDto { + public sealed interface RecordSearchResult permits RecordDto, VoteDto { String type(); String postDate(); int page(); diff --git a/src/main/java/konkuk/thip/record/adapter/in/web/response/VoteDto.java b/src/main/java/konkuk/thip/record/adapter/in/web/response/VoteDto.java index f52ebaab9..12de0cb5f 100644 --- a/src/main/java/konkuk/thip/record/adapter/in/web/response/VoteDto.java +++ b/src/main/java/konkuk/thip/record/adapter/in/web/response/VoteDto.java @@ -21,7 +21,7 @@ public record VoteDto( boolean isWriter, Long voteId, List voteItems -) implements RecordSearchResponse.PostDto { +) implements RecordSearchResponse.RecordSearchResult { @Override public String type() { return "VOTE"; @@ -47,6 +47,23 @@ public static VoteDto of( .build(); } + public VoteDto withIsLikedAndVoteItems(boolean isLiked, List voteItems) { + return new VoteDto( + postDate, + page, + userId, + nickName, + profileImageUrl, + content, + likeCount, + commentCount, + isLiked, + isWriter, + voteId, + voteItems + ); + } + public record VoteItemDto( Long voteItemId, String itemName, From 93e4aacb71e28b14b10f9dc33a76290b917e8dc5 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 02:44:37 +0900 Subject: [PATCH 05/31] =?UTF-8?q?[refactor]=20RecordSearchQuery=20?= =?UTF-8?q?=EB=8C=80=EC=8B=A0=20=EA=B0=92=EC=9C=BC=EB=A1=9C=20=ED=8C=8C?= =?UTF-8?q?=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=A0=84=EB=8B=AC=20(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/web/RecordQueryController.java | 14 +------------- .../port/in/dto/RecordSearchQuery.java | 15 --------------- .../port/in/dto/RecordSearchUseCase.java | 2 +- 3 files changed, 2 insertions(+), 29 deletions(-) delete mode 100644 src/main/java/konkuk/thip/record/application/port/in/dto/RecordSearchQuery.java diff --git a/src/main/java/konkuk/thip/record/adapter/in/web/RecordQueryController.java b/src/main/java/konkuk/thip/record/adapter/in/web/RecordQueryController.java index e86c74b54..93162c9c7 100644 --- a/src/main/java/konkuk/thip/record/adapter/in/web/RecordQueryController.java +++ b/src/main/java/konkuk/thip/record/adapter/in/web/RecordQueryController.java @@ -3,7 +3,6 @@ import konkuk.thip.common.dto.BaseResponse; import konkuk.thip.common.security.annotation.UserId; import konkuk.thip.record.adapter.in.web.response.RecordSearchResponse; -import konkuk.thip.record.application.port.in.dto.RecordSearchQuery; import konkuk.thip.record.application.port.in.dto.RecordSearchUseCase; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; @@ -28,18 +27,7 @@ public BaseResponse viewRecordList( @RequestParam final Integer pageNum, @UserId final Long userId ) { - return BaseResponse.ok(recordSearchUseCase.search( - RecordSearchQuery.builder() - .roomId(roomId) - .type(type) - .sort(sort) - .pageStart(pageStart) - .pageEnd(pageEnd) - .isOverview(isOverview) - .pageNum(pageNum) - .userId(userId) - .build() - )); + return BaseResponse.ok(recordSearchUseCase.search(roomId, type, sort, pageStart, pageEnd, isOverview, pageNum, userId)); } } diff --git a/src/main/java/konkuk/thip/record/application/port/in/dto/RecordSearchQuery.java b/src/main/java/konkuk/thip/record/application/port/in/dto/RecordSearchQuery.java deleted file mode 100644 index c35f79ca6..000000000 --- a/src/main/java/konkuk/thip/record/application/port/in/dto/RecordSearchQuery.java +++ /dev/null @@ -1,15 +0,0 @@ -package konkuk.thip.record.application.port.in.dto; - -import lombok.Builder; - -@Builder -public record RecordSearchQuery( - Long roomId, - String type, - String sort, - Integer pageStart, - Integer pageEnd, - Boolean isOverview, - Integer pageNum, - Long userId) { -} diff --git a/src/main/java/konkuk/thip/record/application/port/in/dto/RecordSearchUseCase.java b/src/main/java/konkuk/thip/record/application/port/in/dto/RecordSearchUseCase.java index 031d2ed6b..f27e2da69 100644 --- a/src/main/java/konkuk/thip/record/application/port/in/dto/RecordSearchUseCase.java +++ b/src/main/java/konkuk/thip/record/application/port/in/dto/RecordSearchUseCase.java @@ -4,5 +4,5 @@ public interface RecordSearchUseCase { - RecordSearchResponse search(RecordSearchQuery query); + RecordSearchResponse search(Long roomId, String type, String sort, Integer pageStart, Integer pageEnd, Boolean isOverview, Integer pageNum, Long userId); } From 65c4121718227df32a6f6543bb2f325158ffa740 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 03:41:25 +0900 Subject: [PATCH 06/31] =?UTF-8?q?[refactor]=20RoomId=EB=A1=9C=20Book=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8A=94=20=EC=BF=BC=EB=A6=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/BookCommandPersistenceAdapter.java | 11 +++++++++++ .../book/application/port/out/BookCommandPort.java | 2 ++ 2 files changed, 13 insertions(+) diff --git a/src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java b/src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java index 8605655e5..12ee688a9 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java @@ -5,15 +5,18 @@ import konkuk.thip.book.application.port.out.BookCommandPort; import konkuk.thip.book.domain.Book; import konkuk.thip.common.exception.EntityNotFoundException; +import konkuk.thip.room.adapter.out.persistence.RoomJpaRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import static konkuk.thip.common.exception.code.ErrorCode.BOOK_NOT_FOUND; +import static konkuk.thip.common.exception.code.ErrorCode.ROOM_NOT_FOUND; @Repository @RequiredArgsConstructor public class BookCommandPersistenceAdapter implements BookCommandPort { + private final RoomJpaRepository roomJpaRepository; private final BookJpaRepository bookJpaRepository; private final BookMapper bookMapper; @@ -49,4 +52,12 @@ public void updateForPageCount(Book book) { bookJpaEntity.changePageCount(book.getPageCount()); bookJpaRepository.save(bookJpaEntity); } + + @Override + public Book findBookByRoomId(Long roomId) { + BookJpaEntity bookJpaEntity = roomJpaRepository.findById(roomId).orElseThrow( + () -> new EntityNotFoundException(ROOM_NOT_FOUND) + ).getBookJpaEntity(); + return bookMapper.toDomainEntity(bookJpaEntity); + } } diff --git a/src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java b/src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java index c7ce10e8e..bcc32d044 100644 --- a/src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java +++ b/src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java @@ -12,4 +12,6 @@ public interface BookCommandPort { Book findById(Long id); void updateForPageCount(Book book); + + Book findBookByRoomId(Long roomId); } From f893892f68cbd53384abaed1e33d012e8a4fe787 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 03:41:59 +0900 Subject: [PATCH 07/31] =?UTF-8?q?[refactor]=20Querydsl=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95=20=ED=95=84=ED=84=B0=EB=A7=81=20?= =?UTF-8?q?+=20=EC=A0=95=EB=A0=AC=20+=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=20=EC=A0=81=EC=9A=A9=20(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/RecordQueryRepository.java | 8 +- .../RecordQueryRepositoryImpl.java | 133 ++++++++++++++---- 2 files changed, 113 insertions(+), 28 deletions(-) diff --git a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepository.java b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepository.java index e6983e320..af4495ae2 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepository.java +++ b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepository.java @@ -1,11 +1,11 @@ package konkuk.thip.record.adapter.out.persistence; -import konkuk.thip.record.adapter.out.jpa.RecordJpaEntity; - -import java.util.List; +import konkuk.thip.record.adapter.in.web.response.RecordSearchResponse; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; public interface RecordQueryRepository { - List findRecordsByRoom(Long roomId, String type, Integer pageStart, Integer pageEnd, Long userId); + Page findRecordsByRoom(Long roomId, String viewType, Integer pageStart, Integer pageEnd, Boolean isOverview, Long userId, Pageable pageable); } diff --git a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java index 99bdd6de2..789aec5d5 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java +++ b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java @@ -1,46 +1,131 @@ package konkuk.thip.record.adapter.out.persistence; -import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.core.BooleanBuilder; +import com.querydsl.core.types.Order; +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.dsl.NumberTemplate; import com.querydsl.jpa.impl.JPAQueryFactory; +import konkuk.thip.comment.adapter.out.jpa.QCommentJpaEntity; +import konkuk.thip.common.exception.InvalidStateException; +import konkuk.thip.common.exception.code.ErrorCode; +import konkuk.thip.common.util.DateUtil; +import konkuk.thip.post.adapter.out.jpa.PostJpaEntity; +import konkuk.thip.post.adapter.out.jpa.QPostJpaEntity; +import konkuk.thip.post.adapter.out.jpa.QPostLikeJpaEntity; +import konkuk.thip.record.adapter.in.web.response.RecordDto; +import konkuk.thip.record.adapter.in.web.response.RecordSearchResponse; +import konkuk.thip.record.adapter.in.web.response.VoteDto; import konkuk.thip.record.adapter.out.jpa.QRecordJpaEntity; import konkuk.thip.record.adapter.out.jpa.RecordJpaEntity; -import konkuk.thip.user.adapter.out.jpa.QUserJpaEntity; +import konkuk.thip.vote.adapter.out.jpa.QVoteJpaEntity; +import konkuk.thip.vote.adapter.out.jpa.VoteJpaEntity; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Repository; +import java.util.ArrayList; import java.util.List; @Repository @RequiredArgsConstructor public class RecordQueryRepositoryImpl implements RecordQueryRepository { - private final JPAQueryFactory jpaQueryFactory; + private final JPAQueryFactory queryFactory; @Override - public List findRecordsByRoom(Long roomId, String type, Integer pageStart, Integer pageEnd, Long userId) { + public Page findRecordsByRoom(Long roomId, String viewType, Integer pageStart, Integer pageEnd, Boolean isOverview, Long loginUserId, Pageable pageable) { + QPostJpaEntity post = QPostJpaEntity.postJpaEntity; QRecordJpaEntity record = QRecordJpaEntity.recordJpaEntity; - QUserJpaEntity user = QUserJpaEntity.userJpaEntity; - - return jpaQueryFactory - .select(record) - .from(record) - .leftJoin(record.userJpaEntity, user).fetchJoin() - .where( - record.roomJpaEntity.roomId.eq(roomId), - filterByType(type, record, userId), - (startEndNull(pageStart, pageEnd) ? record.isOverview.isTrue() : record.page.between(pageStart, pageEnd)) - ) - .fetch(); - } + QVoteJpaEntity vote = QVoteJpaEntity.voteJpaEntity; - private boolean startEndNull(Integer start, Integer end) { - return start == null && end == null; - } + BooleanBuilder where = new BooleanBuilder(); + where.and(post.instanceOf(RecordJpaEntity.class).and(record.roomJpaEntity.roomId.eq(roomId))) + .or(post.instanceOf(VoteJpaEntity.class).and(vote.roomJpaEntity.roomId.eq(roomId))); + + if (isOverview) { + where.and(post.instanceOf(RecordJpaEntity.class).and(record.isOverview.isTrue())) + .or(post.instanceOf(VoteJpaEntity.class).and(vote.isOverview.isTrue())); + } else { + where.and(post.instanceOf(RecordJpaEntity.class).and(record.page.between(pageStart, pageEnd))) + .or(post.instanceOf(VoteJpaEntity.class).and(vote.page.between(pageStart, pageEnd))); + } + + if ("mine".equals(viewType)) { + where.and(post.userJpaEntity.userId.eq(loginUserId)); + } + + List> orderSpecifiers = new ArrayList<>(); + for (Sort.Order order : pageable.getSort()) { + String property = order.getProperty(); + boolean asc = order.getDirection().isAscending(); - private BooleanExpression filterByType(String type, QRecordJpaEntity post, Long userId) { - if ("mine".equalsIgnoreCase(type)) { - return post.userJpaEntity.userId.eq(userId); + if ("likeCount".equalsIgnoreCase(property)) { + NumberTemplate likeCountTemplate = com.querydsl.core.types.dsl.Expressions.numberTemplate( + Long.class, "count({0})", QPostLikeJpaEntity.postLikeJpaEntity.likeId); + orderSpecifiers.add(new OrderSpecifier<>(asc ? Order.ASC : Order.DESC, likeCountTemplate)); + } else if ("commentCount".equalsIgnoreCase(property)) { + NumberTemplate commentCountTemplate = com.querydsl.core.types.dsl.Expressions.numberTemplate( + Long.class, "count({0})", QCommentJpaEntity.commentJpaEntity.commentId); + orderSpecifiers.add(new OrderSpecifier<>(asc ? Order.ASC : Order.DESC, commentCountTemplate)); + } else if ("createdAt".equalsIgnoreCase(property)) { + orderSpecifiers.add(asc ? post.createdAt.asc() : post.createdAt.desc()); + } else { + orderSpecifiers.add(post.createdAt.desc()); + } } - return null; + + List posts = queryFactory + .selectFrom(post) + .leftJoin(record).on(post.postId.eq(record.postId)) + .leftJoin(vote).on(post.postId.eq(vote.postId)) + .where(where) + .orderBy(orderSpecifiers.toArray(new OrderSpecifier[0])) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + List resultList = posts.stream() + .map(p -> { + if (p instanceof RecordJpaEntity r) { + return new RecordDto( + DateUtil.formatLastActivityTime(r.getCreatedAt()), + r.getPage(), + r.getUserJpaEntity().getUserId(), + r.getUserJpaEntity().getNickname(), + r.getUserJpaEntity().getImageUrl(), + r.getContent(), + r.getLikeCount(), + r.getCommentCount(), + false, + loginUserId.equals(r.getUserJpaEntity().getUserId()), + r.getPostId() + ); + } else if (p instanceof VoteJpaEntity v) { + // VoteItem은 양방향 매핑이 없으므로 빈 리스트로 처리하고 서비스 레벨에서 파싱 + return new VoteDto( + DateUtil.formatLastActivityTime(v.getCreatedAt()), + v.getPage(), + v.getUserJpaEntity().getUserId(), + v.getUserJpaEntity().getNickname(), + v.getUserJpaEntity().getImageUrl(), + v.getContent(), + v.getLikeCount(), + v.getCommentCount(), + false, + loginUserId.equals(v.getUserJpaEntity().getUserId()), + v.getPostId(), + new ArrayList<>() + ); + } else { + throw new InvalidStateException(ErrorCode.API_SERVER_ERROR, new IllegalStateException("지원되지 않는 게시물 타입: " + p.getClass().getSimpleName())); + } + }) + .map(result -> (RecordSearchResponse.RecordSearchResult) result) + .toList(); + + return new PageImpl<>(resultList, pageable, resultList.size()); } } \ No newline at end of file From 11b8dfa0139f364c8959036310167f1fb5029cd1 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 03:42:08 +0900 Subject: [PATCH 08/31] =?UTF-8?q?[refactor]=20Adapter=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EA=B0=84=EC=86=8C=ED=99=94=20(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RecordQueryPersistenceAdapter.java | 21 ++++++------------- .../application/port/out/RecordQueryPort.java | 6 ++++-- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryPersistenceAdapter.java b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryPersistenceAdapter.java index 8cc37ef58..d066af7d5 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryPersistenceAdapter.java @@ -1,17 +1,15 @@ package konkuk.thip.record.adapter.out.persistence; +import konkuk.thip.record.adapter.in.web.response.RecordSearchResponse; import konkuk.thip.record.adapter.out.mapper.RecordMapper; -import konkuk.thip.record.application.port.in.dto.RecordSearchResult; import konkuk.thip.record.application.port.out.RecordQueryPort; -import konkuk.thip.record.domain.Record; import konkuk.thip.vote.adapter.out.mapper.VoteMapper; import konkuk.thip.vote.adapter.out.persistence.VoteJpaRepository; -import konkuk.thip.vote.domain.Vote; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Repository; -import java.util.List; - @Repository @RequiredArgsConstructor public class RecordQueryPersistenceAdapter implements RecordQueryPort { @@ -23,17 +21,10 @@ public class RecordQueryPersistenceAdapter implements RecordQueryPort { private static final Integer PAGE_SIZE = 10; - @Override - public RecordSearchResult findRecordsByRoom(Long roomId, String type, Integer pageStart, Integer pageEnd, Long userId, Integer pageNum) { - List records = recordJpaRepository.findRecordsByRoom(roomId, type, pageStart, pageEnd, userId).stream() - .map(recordMapper::toDomainEntity) - .toList(); - - List votes = voteJpaRepository.findVotesByRoom(roomId, type, pageStart, pageEnd, userId).stream() - .map(voteMapper::toDomainEntity) - .toList(); - return RecordSearchResult.of(records, votes); + @Override + public Page findRecordsByRoom(Long roomId, String type, Integer pageStart, Integer pageEnd, Boolean isOverview, Long userId, Pageable pageable) { + return recordJpaRepository.findRecordsByRoom(roomId, type, pageStart, pageEnd, isOverview, userId, pageable); } } diff --git a/src/main/java/konkuk/thip/record/application/port/out/RecordQueryPort.java b/src/main/java/konkuk/thip/record/application/port/out/RecordQueryPort.java index 98605c46c..aad569a42 100644 --- a/src/main/java/konkuk/thip/record/application/port/out/RecordQueryPort.java +++ b/src/main/java/konkuk/thip/record/application/port/out/RecordQueryPort.java @@ -1,9 +1,11 @@ package konkuk.thip.record.application.port.out; -import konkuk.thip.record.application.port.in.dto.RecordSearchResult; +import konkuk.thip.record.adapter.in.web.response.RecordSearchResponse; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; public interface RecordQueryPort { - RecordSearchResult findRecordsByRoom(Long roomId, String type, Integer pageStart, Integer pageEnd, Long userId, Integer pageNum); + Page findRecordsByRoom(Long roomId, String type, Integer pageStart, Integer pageEnd, Boolean isOverview, Long userId, Pageable pageable); } From 1ee0223cf43e85a5a8cdbf7ce88a6bbded54ba82 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 03:43:03 +0900 Subject: [PATCH 09/31] =?UTF-8?q?[refactor]=20=EC=BF=BC=EB=A6=AC=20?= =?UTF-8?q?=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=83=81=EC=88=98=20?= =?UTF-8?q?=EC=A0=95=EC=9D=98=20=EB=B0=8F=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/RecordSearchSortParams.java | 31 +++++++++++++++++++ .../persistence/RecordSearchTypeParams.java | 29 +++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/main/java/konkuk/thip/record/adapter/out/persistence/RecordSearchSortParams.java create mode 100644 src/main/java/konkuk/thip/record/adapter/out/persistence/RecordSearchTypeParams.java diff --git a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordSearchSortParams.java b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordSearchSortParams.java new file mode 100644 index 000000000..46103e9ed --- /dev/null +++ b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordSearchSortParams.java @@ -0,0 +1,31 @@ +package konkuk.thip.record.adapter.out.persistence; + +import com.sun.jdi.request.InvalidRequestStateException; +import konkuk.thip.common.exception.InvalidStateException; +import konkuk.thip.common.exception.code.ErrorCode; +import lombok.Getter; + +import java.util.Arrays; + +@Getter +public enum RecordSearchSortParams { + + LATEST("latest"), + LIKE("like"), + COMMENT("comment"); + + private final String value; + + RecordSearchSortParams(String value) { + this.value = value; + } + + public static RecordSearchSortParams from(String value) { + return Arrays.stream(RecordSearchSortParams.values()) + .filter(param -> param.getValue().equals(value)) + .findFirst() + .orElseThrow( + () -> new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("현재 정렬 조건 param : " + value)) + ); + } +} diff --git a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordSearchTypeParams.java b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordSearchTypeParams.java new file mode 100644 index 000000000..278121d70 --- /dev/null +++ b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordSearchTypeParams.java @@ -0,0 +1,29 @@ +package konkuk.thip.record.adapter.out.persistence; + +import com.sun.jdi.request.InvalidRequestStateException; +import konkuk.thip.common.exception.InvalidStateException; +import konkuk.thip.common.exception.code.ErrorCode; +import lombok.Getter; + +import java.util.Arrays; + +@Getter +public enum RecordSearchTypeParams { + GROUP("group"), + MINE("mine"); + + private final String value; + + RecordSearchTypeParams(String value) { + this.value = value; + } + + public static RecordSearchTypeParams from(String value) { + return Arrays.stream(RecordSearchTypeParams.values()) + .filter(param -> param.getValue().equals(value)) + .findFirst() + .orElseThrow( + () -> new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("현재 타입 조건 param : " + value)) + ); + } +} From 19e2b5be85a48ed09b7d21309313c1a0e4b46480 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 03:45:22 +0900 Subject: [PATCH 10/31] =?UTF-8?q?[refactor]=20=EC=84=9C=EB=B9=84=EC=8A=A4?= =?UTF-8?q?=20=EB=A0=88=EB=B2=A8=20=EC=88=98=EC=A0=95=20(#53)=20-=20?= =?UTF-8?q?=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=A6=9D=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(isOverview=20=EA=B4=80=EB=A0=A8)=20-=20=EC=A0=95?= =?UTF-8?q?=EB=A0=AC=20=EC=A1=B0=EA=B1=B4=20=ED=99=95=EC=9D=B8=20-=20Pagea?= =?UTF-8?q?ble=20=EA=B0=9D=EC=B2=B4=20=EC=83=9D=EC=84=B1=20-=20isLiked?= =?UTF-8?q?=EC=99=80=20voteItems=20=EC=A7=81=EC=A0=91=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=ED=95=B4=EC=84=9C=20=ED=8C=8C=EC=8B=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/RecordSearchService.java | 201 +++++++++--------- 1 file changed, 98 insertions(+), 103 deletions(-) diff --git a/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java b/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java index 021403843..9c8838eff 100644 --- a/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java +++ b/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java @@ -1,33 +1,30 @@ package konkuk.thip.record.application.service; import com.sun.jdi.request.InvalidRequestStateException; -import konkuk.thip.comment.application.port.out.CommentQueryPort; +import konkuk.thip.book.application.port.out.BookCommandPort; +import konkuk.thip.book.domain.Book; import konkuk.thip.common.exception.InvalidStateException; import konkuk.thip.common.exception.code.ErrorCode; -import konkuk.thip.common.util.DateUtil; import konkuk.thip.post.application.port.out.PostLikeQueryPort; import konkuk.thip.record.adapter.in.web.response.RecordDto; import konkuk.thip.record.adapter.in.web.response.RecordSearchResponse; import konkuk.thip.record.adapter.in.web.response.VoteDto; -import konkuk.thip.record.application.port.in.dto.RecordSearchQuery; -import konkuk.thip.record.application.port.in.dto.RecordSearchResult; +import konkuk.thip.record.adapter.out.persistence.RecordSearchSortParams; +import konkuk.thip.record.adapter.out.persistence.RecordSearchTypeParams; import konkuk.thip.record.application.port.in.dto.RecordSearchUseCase; import konkuk.thip.record.application.port.out.RecordQueryPort; -import konkuk.thip.record.domain.Record; -import konkuk.thip.user.application.port.out.UserCommandPort; -import konkuk.thip.user.domain.User; import konkuk.thip.vote.application.port.out.VoteCommandPort; import konkuk.thip.vote.application.port.out.VoteQueryPort; -import konkuk.thip.vote.domain.Vote; import konkuk.thip.vote.domain.VoteItem; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; -import java.util.ArrayList; -import java.util.Comparator; import java.util.List; -import java.util.Optional; @Slf4j @Service @@ -35,121 +32,119 @@ public class RecordSearchService implements RecordSearchUseCase { private final RecordQueryPort recordQueryPort; - private final UserCommandPort userCommandPort; - private final PostLikeQueryPort postLikeQueryPort; - private final CommentQueryPort commentQueryPort; + private final BookCommandPort bookCommandPort; private final VoteCommandPort voteCommandPort; private final VoteQueryPort voteQueryPort; + private final PostLikeQueryPort postLikeQueryPort; - private final DateUtil dateUtil; - - private static final int PAGE_SIZE = 10; + private static final int DEFAULT_PAGE_SIZE = 10; @Override - public RecordSearchResponse search(RecordSearchQuery query) { - validateQueryParams(query); - - // 1. 파라미터에 따라 Record와 Vote를 조회 - RecordSearchResult recordSearchResult = recordQueryPort.findRecordsByRoom( - query.roomId(), - Optional.ofNullable(query.type()).orElse("group"), - query.pageStart(), - query.pageEnd(), - query.userId(), - query.pageNum() + public RecordSearchResponse search(Long roomId, String type, String sort, Integer pageStart, Integer pageEnd, Boolean isOverview, Integer pageNum, Long userId) { + // 1. 유효성 검사 + validatePageStartAndEnd(pageStart, pageEnd, isOverview); + validatePageNum(pageNum); + + // isOverview가 false일 때 pageStart와 pageEnd가 모두 null이면 전체 페이지 조회 + if(!isOverview && (pageStart == null || pageEnd == null)) { + Book book = bookCommandPort.findBookByRoomId(roomId); + pageStart = 1; + pageEnd = book.getPageCount(); + } + + // 2. 정렬 조건 확인 + RecordSearchSortParams sortVal = sort != null ? RecordSearchSortParams.from(sort) : RecordSearchSortParams.LATEST; + RecordSearchTypeParams typeVal = type != null ? RecordSearchTypeParams.from(type) : RecordSearchTypeParams.GROUP; + + // 3. 페이지 인덱스 보정 + int pageIndex = pageNum > 0 ? pageNum - 1 : 0; + Pageable pageable = PageRequest.of(pageIndex, DEFAULT_PAGE_SIZE, buildSort(sortVal)); + + // 4. 게시글 조회 + Page result = recordQueryPort.findRecordsByRoom( + roomId, + typeVal.getValue(), + pageStart, + pageEnd, + isOverview, + userId, + pageable ); - List records = recordSearchResult.records(); - List votes = recordSearchResult.votes(); + // 5. isLiked와 voteItems를 포함한 최종 결과 리스트 생성 + List finalList = result.getContent().stream() + .map(post -> { + if (post instanceof RecordDto recordDto) { + boolean isLiked = checkIfLiked(recordDto.recordId(), userId); + return recordDto.withIsLiked(isLiked); // withIsLiked 메서드는 builder로 따로 정의 + } else if (post instanceof VoteDto voteDto) { + boolean isLiked = checkIfLiked(voteDto.voteId(), userId); + List items = voteCommandPort.findVoteItemsByVoteId(voteDto.voteId()); + List voteItemDtos = mapToVoteItemDtos(items, userId, voteDto.voteId()); + return voteDto.withIsLikedAndVoteItems(isLiked, voteItemDtos); // builder 또는 커스텀 생성자 필요 + } else { + throw new InvalidStateException(ErrorCode.API_SERVER_ERROR, new IllegalStateException("지원되지 않는 게시물 타입입니다")); + } + }) + .map(finalResult -> (RecordSearchResponse.RecordSearchResult) finalResult) + .toList(); - List combinedPosts = new ArrayList<>(); + // 6. response 구성 + return new RecordSearchResponse( + finalList, + pageNum, + result.getNumberOfElements(), + result.isLast(), + result.isFirst()); + } - // 2. Record와 Vote를 PostDto로 변환하여 combinedPosts에 추가 - for (Record record : records) { - combinedPosts.add(createRecordDto(record, query.userId())); + private void validatePageStartAndEnd(Integer pageStart, Integer pageEnd, Boolean isOverview) { + if((pageStart != null && pageEnd == null) || (pageStart == null && pageEnd != null)) { + throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("pageStart와 pageEnd는 모두 설정되거나(특정 페이지 조회) 모두 설정되지 않아야 합니다.(전체 페이지 조회)")); } - - for (Vote vote : votes) { - combinedPosts.add(createVoteDto(vote, query.userId())); + if (pageStart != null && pageEnd != null && pageStart > pageEnd) { + throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("pageStart는 pageEnd보다 작거나 같아야 합니다.")); } - - // 3. sort에 따라 정렬 (기본값은 "latest") - String sort = Optional.ofNullable(query.sort()).orElse("latest"); - sortCombinedPosts(sort, combinedPosts); - - // 4. 페이지네이션 변수 설정 - int pageNum = Optional.ofNullable(query.pageNum()).orElse(1); - int pageSize = PAGE_SIZE; - int fromIndex = (pageNum - 1) * pageSize; - int toIndex = Math.min(fromIndex + pageSize, combinedPosts.size()); - - // 5. 페이지 범위에 따라 서브리스트 생성 - List pagedList = fromIndex >= combinedPosts.size() ? new ArrayList<>() : combinedPosts.subList(fromIndex, toIndex); - - boolean isFirst = pageNum == 1; - boolean isLast = toIndex >= combinedPosts.size(); - if (isLast) { - pageSize = pagedList.size(); // 마지막 페이지일 경우 페이지 크기 업데이트 + if(!isOverview && (pageStart == null || pageEnd == null)) { + throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("pageStart와 pageEnd는 isOverview가 false일 때 필수 파라미터입니다.")); + } + if (isOverview && (pageStart != null || pageEnd != null)) { + throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("pageStart와 pageEnd는 isOverview가 true일 때 유효한 파라미터가 아닙니다.")); } - - return RecordSearchResponse.of(pagedList, pageNum, pageSize, isFirst, isLast); } - private void validateQueryParams(RecordSearchQuery query) { - if(query.pageStart() != null && query.pageEnd() == null || query.pageStart() == null && query.pageEnd() != null) { - throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("pageStart와 pageEnd는 모두 설정되어야 합니다.")); + private void validatePageNum(Integer pageNum) { + if (pageNum == null) { + throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("pageNum은 필수 파라미터입니다.")); } - - if(query.pageNum() != null && query.pageNum() < 1) { + if (pageNum < 1) { throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("pageNum은 1 이상의 값이어야 합니다.")); } - - if(query.sort() != null && !List.of("latest", "like", "comment").contains(query.sort())) { - throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("sort는 'latest', 'like', 'comment' 중 하나여야 합니다.")); - } - - if(query.type() != null && !List.of("group", "mine").contains(query.type())) { - throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("type은 'group', 'mine' 중 하나여야 합니다.")); - } } - private RecordSearchResponse.PostDto createRecordDto(Record record, Long userId) { - User user = userCommandPort.findById(record.getCreatorId()); - int likeCount = postLikeQueryPort.countByPostId(record.getId()); - int commentCount = commentQueryPort.countByPostId(record.getId()); - boolean isLiked = postLikeQueryPort.existsByPostIdAndUserId(userId, record.getId()); - boolean isWriter = record.getCreatorId().equals(userId); - return RecordDto.of(record, dateUtil.formatLastActivityTime(record.getCreatedAt()), user, likeCount, commentCount, isLiked, isWriter); + private Sort buildSort(RecordSearchSortParams sort) { + return switch (sort) { + case LIKE -> Sort.by(Sort.Direction.DESC, "likeCount"); + case COMMENT -> Sort.by(Sort.Direction.DESC, "commentCount"); + default -> Sort.by(Sort.Direction.DESC, "createdAt"); + }; } - private RecordSearchResponse.PostDto createVoteDto(Vote vote, Long userId) { - User user = userCommandPort.findById(vote.getCreatorId()); - int likeCount = postLikeQueryPort.countByPostId(vote.getId()); - int commentCount = commentQueryPort.countByPostId(vote.getId()); - boolean isLiked = postLikeQueryPort.existsByPostIdAndUserId(userId, vote.getId()); - boolean isWriter = vote.getCreatorId().equals(userId); - - List voteItems = voteCommandPort.findVoteItemsByVoteId(vote.getId()); - int totalCount = voteItems.stream().mapToInt(VoteItem::getCount).sum(); - - List voteItemDtos = voteItems.stream() - .map(item -> VoteDto.VoteItemDto.of(item, item.calculatePercentage(totalCount), voteQueryPort.isUserVoted(userId, item.getId()))) + private List mapToVoteItemDtos(List items, Long userId, Long voteId) { + int total = items.stream().mapToInt(VoteItem::getCount).sum(); + return items.stream() + .map(item -> VoteDto.VoteItemDto.of( + item, + item.calculatePercentage(total), + voteQueryPort.isUserVoted(userId, voteId) + ) + ) .toList(); - - return VoteDto.of(vote, dateUtil.formatLastActivityTime(vote.getCreatedAt()), user, likeCount, commentCount, isLiked, isWriter, voteItemDtos); } - private void sortCombinedPosts(String sort, List combinedPosts) { - switch (sort) { - case "like" -> combinedPosts.sort( - Comparator.comparingInt(RecordSearchResponse.PostDto::likeCount).reversed() - ); - case "comment" -> combinedPosts.sort( - Comparator.comparingInt(RecordSearchResponse.PostDto::commentCount).reversed() - ); - default -> combinedPosts.sort( - Comparator.comparing(RecordSearchResponse.PostDto::postDate).reversed() - ); - } + private boolean checkIfLiked(Long postId, Long userId) { + return postLikeQueryPort.existsByPostIdAndUserId(postId, userId); } } + + From 13dffdfd801b98f626d417c926a4541bfd6630e8 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 03:46:39 +0900 Subject: [PATCH 11/31] =?UTF-8?q?[refactor]=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20DI=20=EC=82=AD=EC=A0=9C=20(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../out/persistence/RecordQueryPersistenceAdapter.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryPersistenceAdapter.java b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryPersistenceAdapter.java index d066af7d5..2d9ccbbec 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryPersistenceAdapter.java @@ -1,10 +1,7 @@ package konkuk.thip.record.adapter.out.persistence; import konkuk.thip.record.adapter.in.web.response.RecordSearchResponse; -import konkuk.thip.record.adapter.out.mapper.RecordMapper; import konkuk.thip.record.application.port.out.RecordQueryPort; -import konkuk.thip.vote.adapter.out.mapper.VoteMapper; -import konkuk.thip.vote.adapter.out.persistence.VoteJpaRepository; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -15,12 +12,6 @@ public class RecordQueryPersistenceAdapter implements RecordQueryPort { private final RecordJpaRepository recordJpaRepository; - private final VoteJpaRepository voteJpaRepository; - private final RecordMapper recordMapper; - private final VoteMapper voteMapper; - - private static final Integer PAGE_SIZE = 10; - @Override public Page findRecordsByRoom(Long roomId, String type, Integer pageStart, Integer pageEnd, Boolean isOverview, Long userId, Pageable pageable) { From 3b261333c034bdf19bb19fc2fb59fc68c2bb37b3 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 04:01:12 +0900 Subject: [PATCH 12/31] =?UTF-8?q?[test]=20=EA=B8=B0=EB=A1=9D=EC=9E=A5=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20api=20=ED=86=B5=ED=95=A9=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../in/web/RecordQueryControllerTest.java | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/test/java/konkuk/thip/record/adapter/in/web/RecordQueryControllerTest.java b/src/test/java/konkuk/thip/record/adapter/in/web/RecordQueryControllerTest.java index bc5e03386..5d8ff83af 100644 --- a/src/test/java/konkuk/thip/record/adapter/in/web/RecordQueryControllerTest.java +++ b/src/test/java/konkuk/thip/record/adapter/in/web/RecordQueryControllerTest.java @@ -54,7 +54,6 @@ class RecordSearchControllerTest { @Autowired private CategoryJpaRepository categoryJpaRepository; @Autowired private AliasJpaRepository aliasJpaRepository; @Autowired private UserJpaRepository userJpaRepository; - @Autowired private DateUtil dateUtil; @AfterEach void tearDown() { @@ -117,6 +116,8 @@ void record_with_vote_response_success() throws Exception { RecordJpaEntity record = recordJpaRepository.save(RecordJpaEntity.builder() .userJpaEntity(user) .roomJpaEntity(room) + .likeCount(1) + .commentCount(2) .page(1) .content("레코드 내용") .build()); @@ -124,6 +125,8 @@ void record_with_vote_response_success() throws Exception { VoteJpaEntity vote = voteJpaRepository.save(VoteJpaEntity.builder() .userJpaEntity(user) .roomJpaEntity(room) + .likeCount(1) + .commentCount(2) .page(1) .content("투표 내용") .build()); @@ -148,6 +151,7 @@ void record_with_vote_response_success() throws Exception { .param("pageStart", "1") .param("pageEnd", "10") .param("pageNum", "1") + .param("isOverview", "false") .contentType(MediaType.APPLICATION_JSON)); // then @@ -155,27 +159,28 @@ void record_with_vote_response_success() throws Exception { String json = result.andReturn().getResponse().getContentAsString(); JsonNode jsonNode = objectMapper.readTree(json); - JsonNode recordNode = jsonNode.path("data").path("recordList").get(0); - assertThat(recordNode.path("type").asText()).isEqualTo("RECORD"); - assertThat(recordNode.path("page").asInt()).isEqualTo(1); - assertThat(recordNode.path("content").asText()).isEqualTo("레코드 내용"); - assertThat(recordNode.path("nickName").asText()).isEqualTo("사용자"); - assertThat(recordNode.path("postDate").asText()).isEqualTo(dateUtil.formatLastActivityTime(LocalDateTime.now())); - assertThat(recordNode.path("likeCount").asInt()).isEqualTo(0); - assertThat(recordNode.path("commentCount").asInt()).isEqualTo(0); - - JsonNode voteNode = jsonNode.path("data").path("recordList").get(1); + JsonNode voteNode = jsonNode.path("data").path("recordList").get(0); assertThat(voteNode.path("type").asText()).isEqualTo("VOTE"); assertThat(voteNode.path("page").asInt()).isEqualTo(1); assertThat(voteNode.path("content").asText()).isEqualTo("투표 내용"); assertThat(voteNode.path("nickName").asText()).isEqualTo("사용자"); - assertThat(voteNode.path("postDate").asText()).isEqualTo(dateUtil.formatLastActivityTime(LocalDateTime.now())); + assertThat(voteNode.path("postDate").asText()).isEqualTo(DateUtil.formatLastActivityTime(LocalDateTime.now())); JsonNode voteItems = voteNode.path("voteItems"); assertThat(voteItems).hasSize(2); assertThat(voteItems.get(0).get("itemName").asText()).isEqualTo("찬성"); assertThat(voteItems.get(0).get("isVoted").asBoolean()).isEqualTo(false); assertThat(voteItems.get(0).get("percentage").asInt()).isEqualTo(75); + + JsonNode recordNode = jsonNode.path("data").path("recordList").get(1); + assertThat(recordNode.path("type").asText()).isEqualTo("RECORD"); + assertThat(recordNode.path("page").asInt()).isEqualTo(1); + assertThat(recordNode.path("content").asText()).isEqualTo("레코드 내용"); + assertThat(recordNode.path("nickName").asText()).isEqualTo("사용자"); + assertThat(recordNode.path("postDate").asText()).isEqualTo(DateUtil.formatLastActivityTime(LocalDateTime.now())); + assertThat(recordNode.path("likeCount").asInt()).isEqualTo(1); + assertThat(recordNode.path("commentCount").asInt()).isEqualTo(2); + } From 09d302718ec9f36d838ba050ea16c24f7cb11a7a Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 16:26:45 +0900 Subject: [PATCH 13/31] =?UTF-8?q?[refactor]=20DateUtil=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/konkuk/thip/common/util/DateUtil.java | 2 +- .../adapter/out/persistence/RecordQueryRepositoryImpl.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/konkuk/thip/common/util/DateUtil.java b/src/main/java/konkuk/thip/common/util/DateUtil.java index 9b97f6a97..fd328913d 100644 --- a/src/main/java/konkuk/thip/common/util/DateUtil.java +++ b/src/main/java/konkuk/thip/common/util/DateUtil.java @@ -10,7 +10,7 @@ public class DateUtil { //마지막 활동 시간 포맷팅 -> ex. 1분 전, 1시간 전, 1일 전 - public static String formatLastActivityTime(LocalDateTime createdAt) { + public static String formatBeforeTime(LocalDateTime createdAt) { long minutes = Duration.between(createdAt, LocalDateTime.now()).toMinutes(); if (minutes < 1) return "방금 전"; if (minutes < 60) return minutes + "분 전"; diff --git a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java index 789aec5d5..a8da1575d 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java +++ b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java @@ -91,7 +91,7 @@ public Page findRecordsByRoom(Long room .map(p -> { if (p instanceof RecordJpaEntity r) { return new RecordDto( - DateUtil.formatLastActivityTime(r.getCreatedAt()), + DateUtil.formatBeforeTime(r.getCreatedAt()), r.getPage(), r.getUserJpaEntity().getUserId(), r.getUserJpaEntity().getNickname(), @@ -106,7 +106,7 @@ public Page findRecordsByRoom(Long room } else if (p instanceof VoteJpaEntity v) { // VoteItem은 양방향 매핑이 없으므로 빈 리스트로 처리하고 서비스 레벨에서 파싱 return new VoteDto( - DateUtil.formatLastActivityTime(v.getCreatedAt()), + DateUtil.formatBeforeTime(v.getCreatedAt()), v.getPage(), v.getUserJpaEntity().getUserId(), v.getUserJpaEntity().getNickname(), From 3278def580eaeb398a87de6345d5cb594161e4a7 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 16:28:19 +0900 Subject: [PATCH 14/31] =?UTF-8?q?[refactor]=20DateUtil=20Component=20?= =?UTF-8?q?=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/konkuk/thip/common/util/DateUtil.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/konkuk/thip/common/util/DateUtil.java b/src/main/java/konkuk/thip/common/util/DateUtil.java index fd328913d..cd27b84f7 100644 --- a/src/main/java/konkuk/thip/common/util/DateUtil.java +++ b/src/main/java/konkuk/thip/common/util/DateUtil.java @@ -1,12 +1,9 @@ package konkuk.thip.common.util; -import org.springframework.stereotype.Component; - import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; -@Component public class DateUtil { //마지막 활동 시간 포맷팅 -> ex. 1분 전, 1시간 전, 1일 전 From 34fb5cc2c4c79236cb343e52a416956991201e94 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 16:43:47 +0900 Subject: [PATCH 15/31] =?UTF-8?q?[refactor]=20pageNum=EC=9D=B4=20null?= =?UTF-8?q?=EC=9D=B8=20=EA=B2=BD=EC=9A=B0=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95=20(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/service/RecordSearchService.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java b/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java index 9c8838eff..1bf71fbca 100644 --- a/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java +++ b/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java @@ -43,7 +43,7 @@ public class RecordSearchService implements RecordSearchUseCase { public RecordSearchResponse search(Long roomId, String type, String sort, Integer pageStart, Integer pageEnd, Boolean isOverview, Integer pageNum, Long userId) { // 1. 유효성 검사 validatePageStartAndEnd(pageStart, pageEnd, isOverview); - validatePageNum(pageNum); + pageNum = validatePageNum(pageNum); // isOverview가 false일 때 pageStart와 pageEnd가 모두 null이면 전체 페이지 조회 if(!isOverview && (pageStart == null || pageEnd == null)) { @@ -56,8 +56,8 @@ public RecordSearchResponse search(Long roomId, String type, String sort, Intege RecordSearchSortParams sortVal = sort != null ? RecordSearchSortParams.from(sort) : RecordSearchSortParams.LATEST; RecordSearchTypeParams typeVal = type != null ? RecordSearchTypeParams.from(type) : RecordSearchTypeParams.GROUP; - // 3. 페이지 인덱스 보정 - int pageIndex = pageNum > 0 ? pageNum - 1 : 0; + // 3. 페이지 인덱스 및 Pageable 객체 생성 + int pageIndex = pageNum - 1; Pageable pageable = PageRequest.of(pageIndex, DEFAULT_PAGE_SIZE, buildSort(sortVal)); // 4. 게시글 조회 @@ -105,21 +105,19 @@ private void validatePageStartAndEnd(Integer pageStart, Integer pageEnd, Boolean if (pageStart != null && pageEnd != null && pageStart > pageEnd) { throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("pageStart는 pageEnd보다 작거나 같아야 합니다.")); } - if(!isOverview && (pageStart == null || pageEnd == null)) { - throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("pageStart와 pageEnd는 isOverview가 false일 때 필수 파라미터입니다.")); - } if (isOverview && (pageStart != null || pageEnd != null)) { throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("pageStart와 pageEnd는 isOverview가 true일 때 유효한 파라미터가 아닙니다.")); } } - private void validatePageNum(Integer pageNum) { + private Integer validatePageNum(Integer pageNum) { if (pageNum == null) { - throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("pageNum은 필수 파라미터입니다.")); + return 1; // 기본값으로 첫 페이지 반환 } if (pageNum < 1) { throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("pageNum은 1 이상의 값이어야 합니다.")); } + return pageNum; } private Sort buildSort(RecordSearchSortParams sort) { From 256661d865e1f6558f960ddc17683d0ea6e217e6 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 16:44:02 +0900 Subject: [PATCH 16/31] =?UTF-8?q?[test]=20RecordSearchService=20=EB=8B=A8?= =?UTF-8?q?=EC=9C=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/RecordSearchServiceTest.java | 298 ++++++------------ 1 file changed, 104 insertions(+), 194 deletions(-) diff --git a/src/test/java/konkuk/thip/record/application/service/RecordSearchServiceTest.java b/src/test/java/konkuk/thip/record/application/service/RecordSearchServiceTest.java index 18a81b3fa..51ddd5f89 100644 --- a/src/test/java/konkuk/thip/record/application/service/RecordSearchServiceTest.java +++ b/src/test/java/konkuk/thip/record/application/service/RecordSearchServiceTest.java @@ -1,27 +1,21 @@ package konkuk.thip.record.application.service; -import konkuk.thip.comment.application.port.out.CommentQueryPort; +import konkuk.thip.book.application.port.out.BookCommandPort; +import konkuk.thip.book.domain.Book; import konkuk.thip.common.exception.InvalidStateException; -import konkuk.thip.common.util.DateUtil; import konkuk.thip.post.application.port.out.PostLikeQueryPort; +import konkuk.thip.record.adapter.in.web.response.RecordDto; import konkuk.thip.record.adapter.in.web.response.RecordSearchResponse; -import konkuk.thip.record.application.port.in.dto.RecordSearchQuery; -import konkuk.thip.record.application.port.in.dto.RecordSearchResult; import konkuk.thip.record.application.port.out.RecordQueryPort; -import konkuk.thip.record.domain.Record; -import konkuk.thip.user.application.port.out.UserCommandPort; -import konkuk.thip.user.domain.User; import konkuk.thip.vote.application.port.out.VoteCommandPort; import konkuk.thip.vote.application.port.out.VoteQueryPort; -import konkuk.thip.vote.domain.Vote; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageImpl; -import java.time.LocalDateTime; import java.util.List; -import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.*; @@ -30,224 +24,140 @@ class RecordSearchServiceTest { private RecordQueryPort recordQueryPort; - private UserCommandPort userCommandPort; - private PostLikeQueryPort postLikeQueryPort; - private CommentQueryPort commentQueryPort; + private BookCommandPort bookCommandPort; private VoteCommandPort voteCommandPort; private VoteQueryPort voteQueryPort; - private DateUtil dateUtil; + private PostLikeQueryPort postLikeQueryPort; private RecordSearchService recordSearchService; - private final Long userId = 1L; - @BeforeEach void setUp() { recordQueryPort = mock(RecordQueryPort.class); - userCommandPort = mock(UserCommandPort.class); - postLikeQueryPort = mock(PostLikeQueryPort.class); - commentQueryPort = mock(CommentQueryPort.class); + bookCommandPort = mock(BookCommandPort.class); voteCommandPort = mock(VoteCommandPort.class); voteQueryPort = mock(VoteQueryPort.class); - dateUtil = mock(DateUtil.class); + postLikeQueryPort = mock(PostLikeQueryPort.class); recordSearchService = new RecordSearchService( - recordQueryPort, - userCommandPort, - postLikeQueryPort, - commentQueryPort, - voteCommandPort, - voteQueryPort, - dateUtil - ); + recordQueryPort, bookCommandPort, voteCommandPort, voteQueryPort, postLikeQueryPort); } @Test - @DisplayName("최신순 정렬이 적용된 결과를 반환한다") - void testSortByLatest() { - // given - Record record = Record.builder() - .id(1L) - .creatorId(userId) - .content("레코드") - .page(1) - .createdAt(LocalDateTime.now().minusMinutes(10)) - .build(); - Vote vote = Vote.builder() - .id(2L) - .creatorId(userId) - .content("투표") - .page(1) - .createdAt(LocalDateTime.now()) - .build(); - - when(recordQueryPort.findRecordsByRoom(any(), any(), any(), any(), any(), any())) - .thenReturn(RecordSearchResult.of(List.of(record), List.of(vote))); - - when(userCommandPort.findById(any())).thenReturn(mock(User.class)); - when(postLikeQueryPort.countByPostId(anyLong())).thenReturn(0); - when(commentQueryPort.countByPostId(anyLong())).thenReturn(0); - when(postLikeQueryPort.existsByPostIdAndUserId(anyLong(), anyLong())).thenReturn(false); - when(voteCommandPort.findVoteItemsByVoteId(anyLong())).thenReturn(emptyList()); - when(voteQueryPort.isUserVoted(anyLong(), anyLong())).thenReturn(false); - when(dateUtil.formatLastActivityTime(any())).thenReturn("방금 전"); - - // when - RecordSearchResponse response = recordSearchService.search(buildQuery("latest", 1, "mine").build()); - - // then - assertThat(response.recordList()).hasSize(2); - assertThat(response.recordList().get(0).type()).isEqualTo("RECORD"); // 최신순이므로 Record가 먼저 - assertThat(response.first()).isTrue(); - assertThat(response.last()).isTrue(); - assertThat(response.page()).isEqualTo(1); + @DisplayName("전체 페이지 조회 성공") + void search_all_pages_success() { + Long roomId = 1L; + String type = "group"; + String sort = "latest"; + Integer pageStart = null; + Integer pageEnd = null; + Boolean isOverview = false; + Integer pageNum = 1; + Long userId = 1L; + + when(bookCommandPort.findBookByRoomId(roomId)).thenReturn(Book.builder().pageCount(100).build()); + RecordDto mockDto = new RecordDto("방금 전", 1, 1L, "사용자", "url", "내용", 1, 1, false, true, 1L); + when(recordQueryPort.findRecordsByRoom(any(), any(), any(), any(), any(), any(), any())) + .thenReturn(new PageImpl<>(List.of(mockDto))); + when(postLikeQueryPort.existsByPostIdAndUserId(1L, userId)).thenReturn(true); + + RecordSearchResponse response = recordSearchService.search(roomId, type, sort, pageStart, pageEnd, isOverview, pageNum, userId); + + assertThat(response.recordList()).hasSize(1); + assertThat(((RecordDto) response.recordList().get(0)).isLiked()).isTrue(); } @Test - @DisplayName("좋아요 순 정렬이 적용된 결과를 반환한다") - void testSortByLike() { - // given - Record record = Record.builder() - .id(1L) - .creatorId(userId) - .content("레코드") - .page(1) - .createdAt(LocalDateTime.now()) - .build(); - Vote vote = Vote.builder() - .id(2L) - .creatorId(userId) - .content("투표") - .page(1) - .createdAt(LocalDateTime.now()) - .build(); - - when(recordQueryPort.findRecordsByRoom(any(), any(), any(), any(), any(), any())) - .thenReturn(RecordSearchResult.of(List.of(record), List.of(vote))); - - when(userCommandPort.findById(any())).thenReturn(mock(User.class)); - when(postLikeQueryPort.countByPostId(record.getId())).thenReturn(5); - when(postLikeQueryPort.countByPostId(vote.getId())).thenReturn(10); - when(commentQueryPort.countByPostId(anyLong())).thenReturn(0); - when(postLikeQueryPort.existsByPostIdAndUserId(anyLong(), anyLong())).thenReturn(false); - when(voteCommandPort.findVoteItemsByVoteId(anyLong())).thenReturn(emptyList()); - when(voteQueryPort.isUserVoted(anyLong(), anyLong())).thenReturn(false); - when(dateUtil.formatLastActivityTime(any())).thenReturn("방금 전"); - - // when - RecordSearchResponse response = recordSearchService.search(buildQuery("like", 1, "mine").build()); - - // then - assertThat(response.recordList().get(0).type()).isEqualTo("VOTE"); // 좋아요가 더 많음 + @DisplayName("pageStart와 pageEnd가 하나만 null이면 예외 발생") + void invalid_page_range() { + Long roomId = 1L; + String type = null; + String sort = "latest"; + Integer pageStart = 1; + Integer pageEnd = null; + Boolean isOverview = false; + Integer pageNum = 1; + Long userId = 1L; + + assertThrows(InvalidStateException.class, () -> + recordSearchService.search(roomId, type, sort, pageStart, pageEnd, isOverview, pageNum, userId)); } @Test - @DisplayName("페이징 처리가 잘 적용되는지 확인") - void testPagingLogic() { - // given - List records = List.of( - Record.builder().id(1L).creatorId(userId).content("r1").page(1).createdAt(LocalDateTime.now()).build(), - Record.builder().id(2L).creatorId(userId).content("r2").page(2).createdAt(LocalDateTime.now()).build(), - Record.builder().id(3L).creatorId(userId).content("r3").page(3).createdAt(LocalDateTime.now()).build(), - Record.builder().id(4L).creatorId(userId).content("r4").page(4).createdAt(LocalDateTime.now()).build(), - Record.builder().id(5L).creatorId(userId).content("r5").page(5).createdAt(LocalDateTime.now()).build(), - Record.builder().id(6L).creatorId(userId).content("r6").page(6).createdAt(LocalDateTime.now()).build(), - Record.builder().id(7L).creatorId(userId).content("r7").page(7).createdAt(LocalDateTime.now()).build(), - Record.builder().id(8L).creatorId(userId).content("r8").page(8).createdAt(LocalDateTime.now()).build(), - Record.builder().id(9L).creatorId(userId).content("r9").page(9).createdAt(LocalDateTime.now()).build(), - Record.builder().id(10L).creatorId(userId).content("r10").page(10).createdAt(LocalDateTime.now()).build(), - Record.builder().id(11L).creatorId(userId).content("r11").page(11).createdAt(LocalDateTime.now()).build() - ); - - List votes = List.of(); // 투표 없음 - - when(recordQueryPort.findRecordsByRoom(any(), any(), any(), any(), any(), any())) - .thenReturn(RecordSearchResult.of(records, votes)); - - when(userCommandPort.findById(any())).thenReturn(mock(User.class)); - when(postLikeQueryPort.countByPostId(anyLong())).thenReturn(0); - when(commentQueryPort.countByPostId(anyLong())).thenReturn(0); - when(postLikeQueryPort.existsByPostIdAndUserId(anyLong(), anyLong())).thenReturn(false); - when(voteCommandPort.findVoteItemsByVoteId(anyLong())).thenReturn(emptyList()); - when(voteQueryPort.isUserVoted(anyLong(), anyLong())).thenReturn(false); - when(dateUtil.formatLastActivityTime(any())).thenReturn("방금 전"); - - // when - RecordSearchResponse response = recordSearchService.search(buildQuery("latest", 1, "mine").build()); - - // then - assertThat(response.recordList()).hasSize(10); // 페이지 사이즈만큼 반환 - assertThat(response.size()).isEqualTo(10); - assertThat(response.page()).isEqualTo(1); - assertThat(response.first()).isTrue(); - assertThat(response.last()).isFalse(); // 아직 한 개 남음 - } - - private RecordSearchQuery.RecordSearchQueryBuilder buildQuery(String sort, int pageNum, String type) { - return RecordSearchQuery.builder() - .roomId(1L) - .type(type) - .sort(sort) - .pageStart(1) - .pageEnd(10) - .userId(userId) - .pageNum(pageNum); + @DisplayName("pageStart > pageEnd면 예외 발생") + void invalid_page_range_order() { + Long roomId = 1L; + Integer pageStart = 10; + Integer pageEnd = 5; + Boolean isOverview = false; + Integer pageNum = 1; + Long userId = 1L; + + assertThrows(InvalidStateException.class, () -> + recordSearchService.search(roomId, null, null, pageStart, pageEnd, isOverview, pageNum, userId)); } @Test - @DisplayName("pageStart만 설정된 경우 예외가 발생한다") - void testInvalidPageStartOnly() { - // given - RecordSearchQuery query = buildQuery("latest", 1, "mine") - .pageStart(1) - .pageEnd(null) - .build(); - - // when & then - assertThrows(InvalidStateException.class, () -> recordSearchService.search(query)); - } + @DisplayName("pageNum이 1보다 작으면 예외 발생") + void invalid_pageNum() { + Long roomId = 1L; + Integer pageNum = 0; - @Test - @DisplayName("pageEnd만 설정된 경우 예외가 발생한다") - void testInvalidPageEndOnly() { - // given - RecordSearchQuery query = buildQuery("latest", 1, "mine") - .pageStart(null) - .pageEnd(10) - .build(); - - // when & then - assertThrows(InvalidStateException.class, () -> recordSearchService.search(query)); + assertThrows(InvalidStateException.class, () -> + recordSearchService.search(roomId, null, null, null, null, true, pageNum, 1L)); } @Test - @DisplayName("pageNum이 0 이하인 경우 예외가 발생한다") - void testInvalidPageNum() { - // given - RecordSearchQuery query = buildQuery("latest", 0, "mine").build(); + @DisplayName("pageNum이 null인 경우 첫번째 페이지 조회") + void search_with_null_pageNum() { + Long roomId = 1L; + String type = "group"; + String sort = "latest"; + Integer pageStart = null; + Integer pageEnd = null; + Boolean isOverview = true; + Long userId = 1L; + + RecordDto mockDto = new RecordDto("방금 전", 1, 1L, "사용자", "url", "내용", 1, 1, false, true, 1L); + when(recordQueryPort.findRecordsByRoom(any(), any(), any(), any(), any(), any(), any())) + .thenReturn(new PageImpl<>(List.of(mockDto))); + when(postLikeQueryPort.existsByPostIdAndUserId(1L, userId)).thenReturn(true); + + RecordSearchResponse response = recordSearchService.search(roomId, type, sort, pageStart, pageEnd, isOverview, null, userId); - // when & then - assertThrows(InvalidStateException.class, () -> recordSearchService.search(query)); + assertThat(response.page()).isEqualTo(1); + assertThat(response.recordList()).hasSize(1); } @Test - @DisplayName("정의되지 않은 sort 값은 예외를 발생시킨다") - void testInvalidSort() { - // given - RecordSearchQuery query = buildQuery("invalidSort", 1, "mine").build(); - - // when & then - assertThrows(InvalidStateException.class, () -> recordSearchService.search(query)); + @DisplayName("형식이 맞지 않는 type으로 조회 시 예외 발생") + void search_with_invalid_type() { + Long roomId = 1L; + String type = "invalidType"; + String sort = "latest"; + Integer pageStart = 1; + Integer pageEnd = 10; + Boolean isOverview = true; + Integer pageNum = 1; + Long userId = 1L; + + assertThrows(InvalidStateException.class, () -> + recordSearchService.search(roomId, type, sort, pageStart, pageEnd, isOverview, pageNum, userId)); } @Test - @DisplayName("정의되지 않은 type 값은 예외를 발생시킨다") - void testInvalidType() { - // given - RecordSearchQuery query = buildQuery("latest", 1, "invalidType") - .build(); - - // when & then - assertThrows(InvalidStateException.class, () -> recordSearchService.search(query)); + @DisplayName("형식이 맞지 않는 sort로 조회 시 예외 발생") + void search_with_invalid_sort() { + Long roomId = 1L; + String type = "group"; + String sort = "invalidSort"; + Integer pageStart = 1; + Integer pageEnd = 10; + Boolean isOverview = true; + Integer pageNum = 1; + Long userId = 1L; + + assertThrows(InvalidStateException.class, () -> + recordSearchService.search(roomId, type, sort, pageStart, pageEnd, isOverview, pageNum, userId)); } } \ No newline at end of file From 4d93575c08792c92a1315a500a62678c5201a4d5 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 16:44:44 +0900 Subject: [PATCH 17/31] =?UTF-8?q?[test]=20RecordQueryController=20?= =?UTF-8?q?=ED=86=B5=ED=95=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/record/adapter/in/web/RecordQueryControllerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/konkuk/thip/record/adapter/in/web/RecordQueryControllerTest.java b/src/test/java/konkuk/thip/record/adapter/in/web/RecordQueryControllerTest.java index 5d8ff83af..434b77cde 100644 --- a/src/test/java/konkuk/thip/record/adapter/in/web/RecordQueryControllerTest.java +++ b/src/test/java/konkuk/thip/record/adapter/in/web/RecordQueryControllerTest.java @@ -164,7 +164,7 @@ void record_with_vote_response_success() throws Exception { assertThat(voteNode.path("page").asInt()).isEqualTo(1); assertThat(voteNode.path("content").asText()).isEqualTo("투표 내용"); assertThat(voteNode.path("nickName").asText()).isEqualTo("사용자"); - assertThat(voteNode.path("postDate").asText()).isEqualTo(DateUtil.formatLastActivityTime(LocalDateTime.now())); + assertThat(voteNode.path("postDate").asText()).isEqualTo(DateUtil.formatBeforeTime(LocalDateTime.now())); JsonNode voteItems = voteNode.path("voteItems"); assertThat(voteItems).hasSize(2); @@ -177,7 +177,7 @@ void record_with_vote_response_success() throws Exception { assertThat(recordNode.path("page").asInt()).isEqualTo(1); assertThat(recordNode.path("content").asText()).isEqualTo("레코드 내용"); assertThat(recordNode.path("nickName").asText()).isEqualTo("사용자"); - assertThat(recordNode.path("postDate").asText()).isEqualTo(DateUtil.formatLastActivityTime(LocalDateTime.now())); + assertThat(recordNode.path("postDate").asText()).isEqualTo(DateUtil.formatBeforeTime(LocalDateTime.now())); assertThat(recordNode.path("likeCount").asInt()).isEqualTo(1); assertThat(recordNode.path("commentCount").asInt()).isEqualTo(2); From d0317c330e7c3f32de8674330e448263447ec056 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 17:17:27 +0900 Subject: [PATCH 18/31] =?UTF-8?q?[refactor]=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EC=A0=95=EC=A0=81=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/web/response/RecordDto.java | 18 ------ .../adapter/in/web/response/VoteDto.java | 22 ------- .../RecordQueryRepositoryImpl.java | 62 ++++++++++--------- 3 files changed, 34 insertions(+), 68 deletions(-) diff --git a/src/main/java/konkuk/thip/record/adapter/in/web/response/RecordDto.java b/src/main/java/konkuk/thip/record/adapter/in/web/response/RecordDto.java index fa218ea7a..f5c04298e 100644 --- a/src/main/java/konkuk/thip/record/adapter/in/web/response/RecordDto.java +++ b/src/main/java/konkuk/thip/record/adapter/in/web/response/RecordDto.java @@ -1,7 +1,5 @@ package konkuk.thip.record.adapter.in.web.response; -import konkuk.thip.record.domain.Record; -import konkuk.thip.user.domain.User; import lombok.Builder; @Builder @@ -23,22 +21,6 @@ public String type() { return "RECORD"; } - public static RecordDto of(Record record, String postDate, User user, int likeCount, int commentCount, boolean isLiked, boolean isWriter) { - return RecordDto.builder() - .postDate(postDate) - .page(record.getPage()) - .userId(record.getCreatorId()) - .nickName(user.getNickname()) - .profileImageUrl(user.getImageUrl()) - .content(record.getContent()) - .likeCount(likeCount) - .commentCount(commentCount) - .isLiked(isLiked) - .isWriter(isWriter) - .recordId(record.getId()) - .build(); - } - public RecordDto withIsLiked(boolean isLiked) { return new RecordDto( postDate, diff --git a/src/main/java/konkuk/thip/record/adapter/in/web/response/VoteDto.java b/src/main/java/konkuk/thip/record/adapter/in/web/response/VoteDto.java index 12de0cb5f..6907ddde1 100644 --- a/src/main/java/konkuk/thip/record/adapter/in/web/response/VoteDto.java +++ b/src/main/java/konkuk/thip/record/adapter/in/web/response/VoteDto.java @@ -1,7 +1,5 @@ package konkuk.thip.record.adapter.in.web.response; -import konkuk.thip.user.domain.User; -import konkuk.thip.vote.domain.Vote; import konkuk.thip.vote.domain.VoteItem; import lombok.Builder; @@ -27,26 +25,6 @@ public String type() { return "VOTE"; } - public static VoteDto of( - Vote vote, String postDate, User user, int likeCount, int commentCount, boolean isLiked, boolean isWriter, - List voteItems - ) { - return VoteDto.builder() - .postDate(postDate) - .page(vote.getPage()) - .userId(vote.getCreatorId()) - .nickName(user.getNickname()) - .profileImageUrl(user.getImageUrl()) - .content(vote.getContent()) - .likeCount(likeCount) - .commentCount(commentCount) - .isLiked(isLiked) - .isWriter(isWriter) - .voteId(vote.getId()) - .voteItems(voteItems) - .build(); - } - public VoteDto withIsLikedAndVoteItems(boolean isLiked, List voteItems) { return new VoteDto( postDate, diff --git a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java index a8da1575d..7d8e12213 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java +++ b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java @@ -90,35 +90,32 @@ public Page findRecordsByRoom(Long room List resultList = posts.stream() .map(p -> { if (p instanceof RecordJpaEntity r) { - return new RecordDto( - DateUtil.formatBeforeTime(r.getCreatedAt()), - r.getPage(), - r.getUserJpaEntity().getUserId(), - r.getUserJpaEntity().getNickname(), - r.getUserJpaEntity().getImageUrl(), - r.getContent(), - r.getLikeCount(), - r.getCommentCount(), - false, - loginUserId.equals(r.getUserJpaEntity().getUserId()), - r.getPostId() - ); + return RecordDto.builder() + .postDate(DateUtil.formatBeforeTime(r.getCreatedAt())) + .page(r.getPage()) + .userId(r.getUserJpaEntity().getUserId()) + .nickName(r.getUserJpaEntity().getNickname()) + .profileImageUrl(r.getUserJpaEntity().getImageUrl()) + .content(r.getContent()) + .likeCount(r.getLikeCount()) + .commentCount(r.getCommentCount()) + .isLiked(false) // 초기값은 false, 서비스 레벨에서 처리 + .isWriter(loginUserId.equals(r.getUserJpaEntity().getUserId())) + .recordId(r.getPostId()); } else if (p instanceof VoteJpaEntity v) { // VoteItem은 양방향 매핑이 없으므로 빈 리스트로 처리하고 서비스 레벨에서 파싱 - return new VoteDto( - DateUtil.formatBeforeTime(v.getCreatedAt()), - v.getPage(), - v.getUserJpaEntity().getUserId(), - v.getUserJpaEntity().getNickname(), - v.getUserJpaEntity().getImageUrl(), - v.getContent(), - v.getLikeCount(), - v.getCommentCount(), - false, - loginUserId.equals(v.getUserJpaEntity().getUserId()), - v.getPostId(), - new ArrayList<>() - ); + return VoteDto.builder() + .postDate(DateUtil.formatBeforeTime(v.getCreatedAt())) + .page(v.getPage()) + .userId(v.getUserJpaEntity().getUserId()) + .nickName(v.getUserJpaEntity().getNickname()) + .profileImageUrl(v.getUserJpaEntity().getImageUrl()) + .content(v.getContent()) + .likeCount(v.getLikeCount()) + .commentCount(v.getCommentCount()) + .isLiked(false) // 초기값은 false, 서비스 레벨에서 처리 + .isWriter(loginUserId.equals(v.getUserJpaEntity().getUserId())) + .voteId(v.getPostId()); } else { throw new InvalidStateException(ErrorCode.API_SERVER_ERROR, new IllegalStateException("지원되지 않는 게시물 타입: " + p.getClass().getSimpleName())); } @@ -126,6 +123,15 @@ public Page findRecordsByRoom(Long room .map(result -> (RecordSearchResponse.RecordSearchResult) result) .toList(); - return new PageImpl<>(resultList, pageable, resultList.size()); + Long totalCount = queryFactory + .select(post.count()) + .from(post) + .leftJoin(record).on(post.postId.eq(record.postId)) + .leftJoin(vote).on(post.postId.eq(vote.postId)) + .where(where) + .fetchOne(); + long total = (totalCount != null) ? totalCount : 0L; + + return new PageImpl<>(resultList, pageable, total); } } \ No newline at end of file From 7bcd7b38578c871b251154ab14ab2f3261c7eeb9 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 17:17:46 +0900 Subject: [PATCH 19/31] =?UTF-8?q?[chore]=20VoteItem=20percentage=20?= =?UTF-8?q?=EA=B3=84=EC=82=B0=20=EC=95=8C=EA=B3=A0=EB=A6=AC=EC=A6=98=20tod?= =?UTF-8?q?o=20=EC=B6=94=EA=B0=80=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/konkuk/thip/vote/domain/VoteItem.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/konkuk/thip/vote/domain/VoteItem.java b/src/main/java/konkuk/thip/vote/domain/VoteItem.java index 240be475d..1aa575f81 100644 --- a/src/main/java/konkuk/thip/vote/domain/VoteItem.java +++ b/src/main/java/konkuk/thip/vote/domain/VoteItem.java @@ -25,6 +25,7 @@ public static VoteItem withoutId(String itemName, int count, Long voteId) { .build(); } + //todo 총 퍼센트가 100이 되는 알고리즘으로 수정! public int calculatePercentage(int totalCount) { return totalCount == 0 ? 0 : (int) Math.round((this.count * 100.0) / totalCount); } From 66bc8ee2d27d49e3c822966026021c9467511142 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 17:17:59 +0900 Subject: [PATCH 20/31] =?UTF-8?q?[chore]=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/record/application/service/RecordSearchService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java b/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java index 1bf71fbca..f5f009267 100644 --- a/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java +++ b/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java @@ -76,12 +76,12 @@ public RecordSearchResponse search(Long roomId, String type, String sort, Intege .map(post -> { if (post instanceof RecordDto recordDto) { boolean isLiked = checkIfLiked(recordDto.recordId(), userId); - return recordDto.withIsLiked(isLiked); // withIsLiked 메서드는 builder로 따로 정의 + return recordDto.withIsLiked(isLiked); } else if (post instanceof VoteDto voteDto) { boolean isLiked = checkIfLiked(voteDto.voteId(), userId); List items = voteCommandPort.findVoteItemsByVoteId(voteDto.voteId()); List voteItemDtos = mapToVoteItemDtos(items, userId, voteDto.voteId()); - return voteDto.withIsLikedAndVoteItems(isLiked, voteItemDtos); // builder 또는 커스텀 생성자 필요 + return voteDto.withIsLikedAndVoteItems(isLiked, voteItemDtos); } else { throw new InvalidStateException(ErrorCode.API_SERVER_ERROR, new IllegalStateException("지원되지 않는 게시물 타입입니다")); } From 68cdb0ed59b116438992cc241c42b3f762f3a256 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 17:32:20 +0900 Subject: [PATCH 21/31] =?UTF-8?q?[refactor]=20=EA=B0=80=EB=8F=85=EC=84=B1?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=B4=20builder=20=ED=8C=A8=ED=84=B4?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/out/persistence/RecordQueryRepositoryImpl.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java index 7d8e12213..03ebf6c60 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java +++ b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java @@ -101,7 +101,8 @@ public Page findRecordsByRoom(Long room .commentCount(r.getCommentCount()) .isLiked(false) // 초기값은 false, 서비스 레벨에서 처리 .isWriter(loginUserId.equals(r.getUserJpaEntity().getUserId())) - .recordId(r.getPostId()); + .recordId(r.getPostId()) + .build(); } else if (p instanceof VoteJpaEntity v) { // VoteItem은 양방향 매핑이 없으므로 빈 리스트로 처리하고 서비스 레벨에서 파싱 return VoteDto.builder() @@ -115,7 +116,9 @@ public Page findRecordsByRoom(Long room .commentCount(v.getCommentCount()) .isLiked(false) // 초기값은 false, 서비스 레벨에서 처리 .isWriter(loginUserId.equals(v.getUserJpaEntity().getUserId())) - .voteId(v.getPostId()); + .voteId(v.getPostId()) + .voteItems(new ArrayList<>()) // 빈 리스트로 초기화, 서비스 레벨에서 처리 + .build(); } else { throw new InvalidStateException(ErrorCode.API_SERVER_ERROR, new IllegalStateException("지원되지 않는 게시물 타입: " + p.getClass().getSimpleName())); } From 6af0a96b9678da04e165ce0d19ddd9437c8a33d5 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 17:32:36 +0900 Subject: [PATCH 22/31] =?UTF-8?q?[refactor]=20Querydsl=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20(=EC=A0=95=EB=A0=AC,?= =?UTF-8?q?=20=ED=95=84=ED=84=B0=EB=A7=81,=20=ED=8E=98=EC=9D=B4=EC=A7=95)?= =?UTF-8?q?=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RecordQueryRepositoryImplTest.java | 190 ++++++++++-------- 1 file changed, 110 insertions(+), 80 deletions(-) diff --git a/src/test/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImplTest.java b/src/test/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImplTest.java index da584fd0b..de2ab3616 100644 --- a/src/test/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImplTest.java +++ b/src/test/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImplTest.java @@ -1,18 +1,23 @@ package konkuk.thip.record.adapter.out.persistence; import jakarta.persistence.EntityManager; +import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import konkuk.thip.common.util.TestEntityFactory; +import konkuk.thip.config.TestQuerydslConfig; +import konkuk.thip.record.adapter.in.web.response.RecordSearchResponse; import konkuk.thip.record.adapter.out.jpa.RecordJpaEntity; import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; import konkuk.thip.room.adapter.out.jpa.RoomJpaEntity; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; -import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; +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.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.test.context.ActiveProfiles; import java.util.List; @@ -21,28 +26,27 @@ @DataJpaTest @ActiveProfiles("test") -@Import(konkuk.thip.config.TestQuerydslConfig.class) +@Import(TestQuerydslConfig.class) @DisplayName("[JPA] RecordQueryRepositoryImpl 테스트") class RecordQueryRepositoryImplTest { @Autowired - private EntityManager em; + private RecordQueryRepositoryImpl recordQueryRepository; @Autowired - private RecordJpaRepository recordJpaRepository; + private EntityManager em; - @Autowired - RecordQueryRepositoryImpl recordQueryRepository; + private RoomJpaEntity room; + private UserJpaEntity user1; + private UserJpaEntity user2; - @Test - @DisplayName("mine 타입일 경우 유저 ID에 해당하는 기록만 조회된다") - void testFindRecordsByRoom_mine() { - // given + @BeforeEach + void setUp() { AliasJpaEntity alias = TestEntityFactory.createAlias(); em.persist(alias); - UserJpaEntity user1 = TestEntityFactory.createUser(alias); - UserJpaEntity user2 = TestEntityFactory.createUser(alias); + user1 = TestEntityFactory.createUser(alias); + user2 = TestEntityFactory.createUser(alias); em.persist(user1); em.persist(user2); @@ -52,94 +56,120 @@ void testFindRecordsByRoom_mine() { CategoryJpaEntity category = TestEntityFactory.createCategory(alias); em.persist(category); - RoomJpaEntity room = TestEntityFactory.createRoom(book, category); + room = TestEntityFactory.createRoom(book, category); em.persist(room); - RecordJpaEntity r1 = RecordJpaEntity.builder() - .userJpaEntity(user1) - .roomJpaEntity(room) - .content("user1의 레코드") - .page(1) - .isOverview(false) - .build(); - - RecordJpaEntity r2 = RecordJpaEntity.builder() - .userJpaEntity(user2) - .roomJpaEntity(room) - .content("user2의 레코드") - .page(1) - .isOverview(false) - .build(); - - em.persist(r1); - em.persist(r2); + for (int i = 0; i < 10; i++) { + RecordJpaEntity record = RecordJpaEntity.builder() + .userJpaEntity(i % 2 == 0 ? user1 : user2) + .roomJpaEntity(room) + .content("레코드 " + i) + .likeCount(1) + .commentCount(1) + .isOverview(false) + .page(1) + .build(); + em.persist(record); + } + + for (int i = 0; i < 10; i++) { + RecordJpaEntity record = RecordJpaEntity.builder() + .userJpaEntity(i % 2 == 0 ? user1 : user2) + .roomJpaEntity(room) + .content("레코드 " + i) + .likeCount(1) + .commentCount(1) + .isOverview(true) + .page(book.getPageCount()) // 총평은 책 전체 페이지로 저장 + .build(); + em.persist(record); + } + em.flush(); em.clear(); + } + +// @AfterEach +// void tearDown() { +// em.createQuery("DELETE FROM RecordJpaEntity").executeUpdate(); +// em.createQuery("DELETE FROM RoomJpaEntity").executeUpdate(); +// em.createQuery("DELETE FROM UserJpaEntity").executeUpdate(); +// em.createQuery("DELETE FROM AliasJpaEntity").executeUpdate(); +// em.createQuery("DELETE FROM CategoryJpaEntity").executeUpdate(); +// em.createQuery("DELETE FROM BookJpaEntity").executeUpdate(); +// } - // when - List result = recordQueryRepository.findRecordsByRoom( + @Test + @DisplayName("기본 조회 및 페이징 동작 확인") + void test_paging() { + Page result = recordQueryRepository.findRecordsByRoom( room.getRoomId(), - "mine", + "group", 1, 1, - user1.getUserId() + false, + user1.getUserId(), + PageRequest.of(0, 5) ); - // then - assertThat(result).hasSize(1); - assertThat(result.get(0).getUserJpaEntity().getUserId()).isEqualTo(user1.getUserId()); - assertThat(result.get(0).getContent()).isEqualTo("user1의 레코드"); + assertThat(result.getNumberOfElements()).isEqualTo(5); + assertThat(result.getTotalElements()).isEqualTo(10); + assertThat(result.isLast()).isFalse(); + assertThat(result.isFirst()).isTrue(); } @Test - @DisplayName("pageStart, pageEnd가 null이면 isOverview가 true인 레코드만 조회된다") - void testFindRecordsByRoom_overview() { - // given - AliasJpaEntity alias = TestEntityFactory.createAlias(); - em.persist(alias); - - UserJpaEntity user = TestEntityFactory.createUser(alias); - em.persist(user); - - BookJpaEntity book = TestEntityFactory.createBook(); - em.persist(book); + @DisplayName("viewType이 mine일 때 user1의 레코드만 조회된다") + void test_viewType_mine() { + Page result = recordQueryRepository.findRecordsByRoom( + room.getRoomId(), + "mine", + 1, + 1, + false, + user1.getUserId(), + PageRequest.of(0, 10) + ); - CategoryJpaEntity category = TestEntityFactory.createCategory(alias); - em.persist(category); + assertThat(result).allSatisfy(record -> + assertThat(record.userId()).isEqualTo(user1.getUserId())); + assertThat(result.getNumberOfElements()).isEqualTo(5); // user1이 작성한 레코드가 5개 + } - RoomJpaEntity room = TestEntityFactory.createRoom(book, category); - em.persist(room); + @Test + @DisplayName("latest 기준 정렬 확인") + void test_sortingBy_latest() { + Page result = recordQueryRepository.findRecordsByRoom( + room.getRoomId(), + null, + 1, + 1, + false, + user1.getUserId(), + PageRequest.of(0, 10, org.springframework.data.domain.Sort.by("createdAt").descending()) + ); - RecordJpaEntity overview = RecordJpaEntity.builder() - .userJpaEntity(user) - .roomJpaEntity(room) - .content("요약 레코드") - .isOverview(true) - .build(); - - RecordJpaEntity normal = RecordJpaEntity.builder() - .userJpaEntity(user) - .roomJpaEntity(room) - .content("일반 레코드") - .isOverview(false) - .build(); - - em.persist(overview); - em.persist(normal); - em.flush(); - em.clear(); + List content = result.getContent(); + for (int i = 1; i < content.size(); i++) { + assertThat(content.get(i - 1).postDate()).isGreaterThanOrEqualTo(content.get(i).postDate()); + } + } - // when - List result = recordQueryRepository.findRecordsByRoom( + @Test + @DisplayName("isOverview가 true일 때 레코드가 총평 기록만 조회된다.") + void test_isOverview_true() { + Page result = recordQueryRepository.findRecordsByRoom( room.getRoomId(), "group", - null, - null, - user.getUserId() + 1, + 1, + true, + user1.getUserId(), + PageRequest.of(0, 10) ); - // then - assertThat(result).hasSize(1); - assertThat(result.get(0).isOverview()).isTrue(); + assertThat(result.getNumberOfElements()).isEqualTo(10); + assertThat(result.getContent()).allSatisfy(record -> + assertThat(record.page()).isEqualTo(room.getBookJpaEntity().getPageCount())); } } \ No newline at end of file From 631f0a09005746833713a72966c3b7f4d9ea46b8 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 17:43:35 +0900 Subject: [PATCH 23/31] =?UTF-8?q?[fix]=20conflict=20=ED=95=B4=EA=B2=B0=20(?= =?UTF-8?q?#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/BookMostSearchService.java | 2 + .../service/BookSearchService.java | 6 +- .../thip/common/exception/code/ErrorCode.java | 9 +- .../adapter/out/jpa/FeedTagJpaEntity.java | 26 +++++ .../feed/adapter/out/mapper/FeedMapper.java | 10 +- .../feed/adapter/out/mapper/TagMapper.java | 31 ------ .../java/konkuk/thip/feed/domain/Tag.java | 41 ++++++-- .../service/RecordSearchService.java | 2 + .../service/RoomSearchService.java | 10 +- .../service/RoomVerifyPasswordService.java | 1 + .../konkuk/thip/room/domain/Category.java | 2 +- .../in/web/request/UserSignupRequest.java | 11 ++- .../response/UserViewAliasChoiceResponse.java | 2 - .../user/adapter/out/mapper/AliasMapper.java | 29 ------ .../user/adapter/out/mapper/UserMapper.java | 6 +- .../AliasCommandPersistenceAdapter.java | 27 ------ .../out/persistence/AliasJpaRepository.java | 4 + .../AliasQueryPersistenceAdapter.java | 18 ---- .../persistence/AliasQueryRepositoryImpl.java | 1 - .../UserCommandPersistenceAdapter.java | 2 +- .../UserQueryPersistenceAdapter.java | 7 ++ .../port/in/dto/UserSignupCommand.java | 2 +- .../in/dto/UserViewAliasChoiceResult.java | 1 - .../port/out/AliasCommandPort.java | 8 -- .../application/port/out/AliasQueryPort.java | 8 -- .../application/port/out/UserQueryPort.java | 4 + .../service/UserSignupService.java | 6 +- .../service/UserViewAliasChoiceService.java | 6 +- .../java/konkuk/thip/user/domain/Alias.java | 31 ++++-- .../java/konkuk/thip/user/domain/User.java | 11 +-- .../in/web/BookChangeSavedControllerTest.java | 7 +- .../web/BookDetailSearchControllerTest.java | 13 +-- .../BookMostSearchedBooksControllerTest.java | 9 +- .../in/web/BookQueryControllerTest.java | 8 +- .../thip/common/util/TestEntityFactory.java | 43 ++++++--- .../adapter/out/jpa/FeedJpaEntityTest.java | 2 +- .../in/web/RecordCreateControllerTest.java | 4 +- .../in/web/RecordQueryControllerTest.java | 12 +-- .../RecordQueryRepositoryImplTest.java | 4 +- .../adapter/in/web/RoomCreateAPITest.java | 16 +--- .../adapter/in/web/RoomSearchApiTest.java | 96 +++++++++++-------- .../in/web/RoomVerifyPasswordAPITest.java | 16 +--- .../adapter/out/jpa/RecordJpaEntityTest.java | 4 +- .../adapter/out/jpa/RoomJpaEntityTest.java | 4 +- .../adapter/out/jpa/VoteJpaEntityTest.java | 4 +- .../in/web/UserSignupControllerTest.java | 32 +++---- .../web/UserVerifyNicknameControllerTest.java | 8 +- .../UserViewAliasChoiceControllerTest.java | 35 ++----- .../adapter/out/jpa/UserJpaEntityTest.java | 4 +- 49 files changed, 288 insertions(+), 357 deletions(-) create mode 100644 src/main/java/konkuk/thip/feed/adapter/out/jpa/FeedTagJpaEntity.java delete mode 100644 src/main/java/konkuk/thip/feed/adapter/out/mapper/TagMapper.java delete mode 100644 src/main/java/konkuk/thip/user/adapter/out/mapper/AliasMapper.java delete mode 100644 src/main/java/konkuk/thip/user/adapter/out/persistence/AliasCommandPersistenceAdapter.java delete mode 100644 src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryPersistenceAdapter.java delete mode 100644 src/main/java/konkuk/thip/user/application/port/out/AliasCommandPort.java delete mode 100644 src/main/java/konkuk/thip/user/application/port/out/AliasQueryPort.java diff --git a/src/main/java/konkuk/thip/book/application/service/BookMostSearchService.java b/src/main/java/konkuk/thip/book/application/service/BookMostSearchService.java index 8555506f2..96f85cd00 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookMostSearchService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookMostSearchService.java @@ -6,6 +6,7 @@ import konkuk.thip.user.application.port.out.UserCommandPort; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.util.List; @@ -18,6 +19,7 @@ public class BookMostSearchService implements BookMostSearchUseCase { private final UserCommandPort userCommandPort; @Override + @Transactional(readOnly = true) public BookMostSearchResult getMostSearchedBooks(Long userId) { userCommandPort.findById(userId); diff --git a/src/main/java/konkuk/thip/book/application/service/BookSearchService.java b/src/main/java/konkuk/thip/book/application/service/BookSearchService.java index d1ec358f8..8fd48ad2d 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookSearchService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookSearchService.java @@ -1,6 +1,5 @@ package konkuk.thip.book.application.service; -import jakarta.transaction.Transactional; import konkuk.thip.book.adapter.out.api.dto.NaverBookParseResult; import konkuk.thip.book.adapter.out.api.dto.NaverDetailBookParseResult; import konkuk.thip.book.application.port.in.BookSearchUseCase; @@ -21,6 +20,7 @@ import konkuk.thip.user.domain.User; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.util.HashSet; @@ -44,9 +44,8 @@ public class BookSearchService implements BookSearchUseCase { private final UserCommandPort userCommandPort; private final BookRedisCommandPort bookRedisCommandPort; - @Override - @Transactional + @Transactional(readOnly = true) public NaverBookParseResult searchBooks(String keyword, int page, Long userId) { if (keyword == null || keyword.isBlank()) { @@ -79,6 +78,7 @@ public NaverBookParseResult searchBooks(String keyword, int page, Long userId) { } @Override + @Transactional(readOnly = true) public BookDetailSearchResult searchDetailBooks(String isbn,Long userId) { //유저정보찾기 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 6c86b08a0..8d363aa16 100644 --- a/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java +++ b/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java @@ -30,6 +30,7 @@ public enum ErrorCode implements ResponseCode { * 60000 : alias error */ ALIAS_NOT_FOUND(HttpStatus.NOT_FOUND, 60001, "존재하지 않는 ALIAS 입니다."), + ALIAS_NAME_NOT_MATCH(HttpStatus.BAD_REQUEST, 60002, "일치하는 칭호 이름이 없습니다."), /** @@ -102,7 +103,13 @@ public enum ErrorCode implements ResponseCode { * 150000 : Category error */ CATEGORY_NOT_FOUND(HttpStatus.NOT_FOUND, 150000, "존재하지 않는 CATEGORY 입니다."), - CATEGORY_NOT_MATCH(HttpStatus.BAD_REQUEST, 150001, "일치하는 카테고리 이름이 없습니다.") + CATEGORY_NOT_MATCH(HttpStatus.BAD_REQUEST, 150001, "일치하는 카테고리 이름이 없습니다."), + + /** + * 160000 : Feed error + */ + FEED_NOT_FOUND(HttpStatus.NOT_FOUND, 160000, "존재하지 않는 FEED 입니다."), + TAG_NAME_NOT_MATCH(HttpStatus.BAD_REQUEST, 160001, "일치하는 태그 이름이 없습니다.") ; diff --git a/src/main/java/konkuk/thip/feed/adapter/out/jpa/FeedTagJpaEntity.java b/src/main/java/konkuk/thip/feed/adapter/out/jpa/FeedTagJpaEntity.java new file mode 100644 index 000000000..3f7f27d38 --- /dev/null +++ b/src/main/java/konkuk/thip/feed/adapter/out/jpa/FeedTagJpaEntity.java @@ -0,0 +1,26 @@ +package konkuk.thip.feed.adapter.out.jpa; + +import jakarta.persistence.*; +import konkuk.thip.common.entity.BaseJpaEntity; +import lombok.*; + +@Entity +@Table(name = "feed_tags") +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Builder +public class FeedTagJpaEntity extends BaseJpaEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long feedTagId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "post_id", nullable = false) + private FeedJpaEntity feedJpaEntity; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "tag_id", nullable = false) + private TagJpaEntity tagJpaEntity; +} diff --git a/src/main/java/konkuk/thip/feed/adapter/out/mapper/FeedMapper.java b/src/main/java/konkuk/thip/feed/adapter/out/mapper/FeedMapper.java index c3600919c..d4106cf3b 100644 --- a/src/main/java/konkuk/thip/feed/adapter/out/mapper/FeedMapper.java +++ b/src/main/java/konkuk/thip/feed/adapter/out/mapper/FeedMapper.java @@ -2,10 +2,14 @@ import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; +import konkuk.thip.feed.adapter.out.jpa.TagJpaEntity; import konkuk.thip.feed.domain.Feed; +import konkuk.thip.feed.domain.Tag; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import org.springframework.stereotype.Component; +import java.util.List; + @Component public class FeedMapper { @@ -21,7 +25,7 @@ public FeedJpaEntity toJpaEntity(Feed feed, UserJpaEntity userJpaEntity, BookJpa .build(); } - public Feed toDomainEntity(FeedJpaEntity feedJpaEntity) { + public Feed toDomainEntity(FeedJpaEntity feedJpaEntity, List tagJpaEntityList) { return Feed.builder() .id(feedJpaEntity.getPostId()) .content(feedJpaEntity.getContent()) @@ -31,6 +35,10 @@ public Feed toDomainEntity(FeedJpaEntity feedJpaEntity) { .likeCount(feedJpaEntity.getLikeCount()) .commentCount(feedJpaEntity.getCommentCount()) .targetBookId(feedJpaEntity.getBookJpaEntity().getBookId()) + .tagList(tagJpaEntityList.stream() + .map(TagJpaEntity::getValue) + .map(Tag::from) + .toList()) .createdAt(feedJpaEntity.getCreatedAt()) .modifiedAt(feedJpaEntity.getModifiedAt()) .status(feedJpaEntity.getStatus()) diff --git a/src/main/java/konkuk/thip/feed/adapter/out/mapper/TagMapper.java b/src/main/java/konkuk/thip/feed/adapter/out/mapper/TagMapper.java deleted file mode 100644 index 20e96cb88..000000000 --- a/src/main/java/konkuk/thip/feed/adapter/out/mapper/TagMapper.java +++ /dev/null @@ -1,31 +0,0 @@ -package konkuk.thip.feed.adapter.out.mapper; - -import konkuk.thip.feed.adapter.out.jpa.TagJpaEntity; -import konkuk.thip.feed.domain.Tag; -import konkuk.thip.post.adapter.out.jpa.PostJpaEntity; -import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; -import org.springframework.stereotype.Component; - -@Component -public class TagMapper { - - public TagJpaEntity toJpaEntity(Tag tag, PostJpaEntity postJpaEntity, CategoryJpaEntity categoryJpaEntity) { - return TagJpaEntity.builder() - .value(tag.getValue()) - .postJpaEntity(postJpaEntity) - .categoryJpaEntity(categoryJpaEntity) - .build(); - } - - public Tag toDomainEntity(TagJpaEntity tagJpaEntity) { - return Tag.builder() - .id(tagJpaEntity.getTagId()) - .value(tagJpaEntity.getValue()) - .targetPostId(tagJpaEntity.getPostJpaEntity().getPostId()) - .categoryId(tagJpaEntity.getCategoryJpaEntity().getCategoryId()) - .createdAt(tagJpaEntity.getCreatedAt()) - .modifiedAt(tagJpaEntity.getModifiedAt()) - .status(tagJpaEntity.getStatus()) - .build(); - } -} diff --git a/src/main/java/konkuk/thip/feed/domain/Tag.java b/src/main/java/konkuk/thip/feed/domain/Tag.java index 81bfe4ce0..f84214409 100644 --- a/src/main/java/konkuk/thip/feed/domain/Tag.java +++ b/src/main/java/konkuk/thip/feed/domain/Tag.java @@ -1,18 +1,39 @@ package konkuk.thip.feed.domain; -import konkuk.thip.common.entity.BaseDomainEntity; +import konkuk.thip.common.exception.InvalidStateException; +import konkuk.thip.room.domain.Category; import lombok.Getter; -import lombok.experimental.SuperBuilder; +import lombok.RequiredArgsConstructor; -@Getter -@SuperBuilder -public class Tag extends BaseDomainEntity { - - private Long id; +import static konkuk.thip.common.exception.code.ErrorCode.TAG_NAME_NOT_MATCH; - private String value; +@Getter +@RequiredArgsConstructor +public enum Tag { + BOOK_RECOMMEND("책추천"), + TODAYS_BOOK("오늘의책"), + READING_LOG("독서기록"), + BOOK_REVIEW("책리뷰"), + QUOTE("책속한줄"), + BOOK_REPORT("독후감"), + LIFE_BOOK("내인생책"), + RE_READ("다시읽고싶은책"), + BOOK_TALK("북토크"), + BOOKSTAGRAM("책스타그램"), + NOVEL("소설추천"), + SELF_IMPROVEMENT("자기계발서"), + PHILOSOPHY("인문학책"), + SCIENCE("과학책"), + ECONOMY("경제책"); - private Long targetPostId; + private final String value; - private Long categoryId; + public static Tag from(String value) { + for (Tag tagName : Tag.values()) { + if (tagName.value.equalsIgnoreCase(value)) { + return tagName; + } + } + throw new InvalidStateException(TAG_NAME_NOT_MATCH); + } } diff --git a/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java b/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java index f5f009267..30ad7aeb0 100644 --- a/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java +++ b/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java @@ -23,6 +23,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.List; @@ -40,6 +41,7 @@ public class RecordSearchService implements RecordSearchUseCase { private static final int DEFAULT_PAGE_SIZE = 10; @Override + @Transactional(readOnly = true) public RecordSearchResponse search(Long roomId, String type, String sort, Integer pageStart, Integer pageEnd, Boolean isOverview, Integer pageNum, Long userId) { // 1. 유효성 검사 validatePageStartAndEnd(pageStart, pageEnd, isOverview); diff --git a/src/main/java/konkuk/thip/room/application/service/RoomSearchService.java b/src/main/java/konkuk/thip/room/application/service/RoomSearchService.java index a3e3d6af9..488dd9df1 100644 --- a/src/main/java/konkuk/thip/room/application/service/RoomSearchService.java +++ b/src/main/java/konkuk/thip/room/application/service/RoomSearchService.java @@ -12,6 +12,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import static konkuk.thip.common.exception.code.ErrorCode.CATEGORY_NOT_FOUND; import static konkuk.thip.common.exception.code.ErrorCode.INVALID_ROOM_SEARCH_SORT; @@ -25,6 +26,7 @@ public class RoomSearchService implements RoomSearchUseCase { private final RoomQueryPort roomQueryPort; @Override + @Transactional(readOnly = true) public RoomSearchResponse searchRoom(String keyword, String category, String sort, int page) { // 1. validation String sortVal = validateSort(sort); @@ -58,12 +60,8 @@ private String validateCategory(String category) { if (category == null || category.isEmpty()) { return ""; } - try { - Category cat = Category.from(category); - return cat.getValue(); - } catch (IllegalArgumentException ex) { - throw new BusinessException(CATEGORY_NOT_FOUND, ex); - } + + return Category.from(category).getValue(); } /** diff --git a/src/main/java/konkuk/thip/room/application/service/RoomVerifyPasswordService.java b/src/main/java/konkuk/thip/room/application/service/RoomVerifyPasswordService.java index 88ead3713..ab183a6b3 100644 --- a/src/main/java/konkuk/thip/room/application/service/RoomVerifyPasswordService.java +++ b/src/main/java/konkuk/thip/room/application/service/RoomVerifyPasswordService.java @@ -16,6 +16,7 @@ public class RoomVerifyPasswordService implements RoomVerifyPasswordUseCase { private final RoomCommandPort roomCommandPort; @Override + @Transactional(readOnly = true) public Void verifyRoomPassword(RoomVerifyPasswordQuery query) { //방 검증 diff --git a/src/main/java/konkuk/thip/room/domain/Category.java b/src/main/java/konkuk/thip/room/domain/Category.java index 80c6fbd18..1ff96bba6 100644 --- a/src/main/java/konkuk/thip/room/domain/Category.java +++ b/src/main/java/konkuk/thip/room/domain/Category.java @@ -17,7 +17,7 @@ public enum Category { * TODO : DB에서 value를 통해 카테고리를 조회하는것보다 id로 조회하는게 성능상 좋으니, id 값도 같이 보관 ?? */ SCIENCE_IT("과학/IT"), - Literature("문학"), + LITERATURE("문학"), ART("예술"), SOCIAL_SCIENCE("사회과학"), HUMANITY("인문학"); diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/request/UserSignupRequest.java b/src/main/java/konkuk/thip/user/adapter/in/web/request/UserSignupRequest.java index fe13a0804..fbe40ac17 100644 --- a/src/main/java/konkuk/thip/user/adapter/in/web/request/UserSignupRequest.java +++ b/src/main/java/konkuk/thip/user/adapter/in/web/request/UserSignupRequest.java @@ -1,12 +1,13 @@ package konkuk.thip.user.adapter.in.web.request; -import jakarta.validation.constraints.*; -import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; import konkuk.thip.user.application.port.in.dto.UserSignupCommand; public record UserSignupRequest( - @NotNull(message = "aliasId는 필수입니다.") - Long aliasId, + @NotBlank(message = "aliasName은 필수입니다.") + String aliasName, @Pattern(regexp = "[가-힣a-zA-Z0-9]+", message = "닉네임은 한글, 영어, 숫자로만 구성되어야 합니다.(공백불가)") @Size(max = 10, message = "닉네임은 최대 10자 입니다.") @@ -14,7 +15,7 @@ public record UserSignupRequest( ) { public UserSignupCommand toCommand(String oAuth2Id) { return UserSignupCommand.builder() - .aliasId(aliasId) + .aliasName(aliasName) .nickname(nickname) .oauth2Id(oAuth2Id) .build(); diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/response/UserViewAliasChoiceResponse.java b/src/main/java/konkuk/thip/user/adapter/in/web/response/UserViewAliasChoiceResponse.java index 8af48cfef..9d6e96881 100644 --- a/src/main/java/konkuk/thip/user/adapter/in/web/response/UserViewAliasChoiceResponse.java +++ b/src/main/java/konkuk/thip/user/adapter/in/web/response/UserViewAliasChoiceResponse.java @@ -9,7 +9,6 @@ public record UserViewAliasChoiceResponse(List aliasChoices) { public static UserViewAliasChoiceResponse of(UserViewAliasChoiceResult result) { List choices = result.aliasChoices().stream() .map(ac -> new AliasChoice( - ac.aliasId(), ac.aliasName(), ac.categoryName(), ac.imageUrl(), @@ -20,7 +19,6 @@ public static UserViewAliasChoiceResponse of(UserViewAliasChoiceResult result) { } public record AliasChoice( - Long aliasId, String aliasName, String categoryName, String imageUrl, diff --git a/src/main/java/konkuk/thip/user/adapter/out/mapper/AliasMapper.java b/src/main/java/konkuk/thip/user/adapter/out/mapper/AliasMapper.java deleted file mode 100644 index eac3e0f45..000000000 --- a/src/main/java/konkuk/thip/user/adapter/out/mapper/AliasMapper.java +++ /dev/null @@ -1,29 +0,0 @@ -package konkuk.thip.user.adapter.out.mapper; - -import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; -import konkuk.thip.user.domain.Alias; -import org.springframework.stereotype.Component; - -@Component -public class AliasMapper { - - public AliasJpaEntity toJpaEntity(Alias alias) { - return AliasJpaEntity.builder() - .value(alias.getValue()) - .imageUrl(alias.getImageUrl()) - .color(alias.getColor()) - .build(); - } - - public Alias toDomainEntity(AliasJpaEntity aliasJpaEntity) { - return Alias.builder() - .id(aliasJpaEntity.getAliasId()) - .value(aliasJpaEntity.getValue()) - .imageUrl(aliasJpaEntity.getImageUrl()) - .color(aliasJpaEntity.getColor()) - .createdAt(aliasJpaEntity.getCreatedAt()) - .modifiedAt(aliasJpaEntity.getModifiedAt()) - .status(aliasJpaEntity.getStatus()) - .build(); - } -} diff --git a/src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java b/src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java index 40ce8d0c3..4d7fd43a4 100644 --- a/src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java +++ b/src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java @@ -3,6 +3,7 @@ import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserRole; +import konkuk.thip.user.domain.Alias; import konkuk.thip.user.domain.User; import org.springframework.stereotype.Component; @@ -12,7 +13,7 @@ public class UserMapper { public UserJpaEntity toJpaEntity(User user, AliasJpaEntity aliasJpaEntity) { return UserJpaEntity.builder() .nickname(user.getNickname()) - .imageUrl(user.getImageUrl()) + .imageUrl(user.getAlias().getImageUrl()) .role(UserRole.from(user.getUserRole())) .oauth2Id(user.getOauth2Id()) .aliasForUserJpaEntity(aliasJpaEntity) @@ -23,10 +24,9 @@ public User toDomainEntity(UserJpaEntity userJpaEntity) { return User.builder() .id(userJpaEntity.getUserId()) .nickname(userJpaEntity.getNickname()) - .imageUrl(userJpaEntity.getImageUrl()) .userRole(userJpaEntity.getRole().getType()) - .aliasId(userJpaEntity.getAliasForUserJpaEntity().getAliasId()) .oauth2Id(userJpaEntity.getOauth2Id()) + .alias(Alias.from(userJpaEntity.getAliasForUserJpaEntity().getValue())) .createdAt(userJpaEntity.getCreatedAt()) .modifiedAt(userJpaEntity.getModifiedAt()) .status(userJpaEntity.getStatus()) diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasCommandPersistenceAdapter.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasCommandPersistenceAdapter.java deleted file mode 100644 index 154feaf72..000000000 --- a/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasCommandPersistenceAdapter.java +++ /dev/null @@ -1,27 +0,0 @@ -package konkuk.thip.user.adapter.out.persistence; - -import konkuk.thip.common.exception.EntityNotFoundException; -import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; -import konkuk.thip.user.adapter.out.mapper.AliasMapper; -import konkuk.thip.user.application.port.out.AliasCommandPort; -import konkuk.thip.user.domain.Alias; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -import static konkuk.thip.common.exception.code.ErrorCode.ALIAS_NOT_FOUND; - -@Repository -@RequiredArgsConstructor -public class AliasCommandPersistenceAdapter implements AliasCommandPort { - - private final AliasMapper aliasMapper; - private final AliasJpaRepository aliasJpaRepository; - - @Override - public Alias findById(Long aliasId) { - AliasJpaEntity aliasJpaEntity = aliasJpaRepository.findById(aliasId).orElseThrow( - () -> new EntityNotFoundException(ALIAS_NOT_FOUND)); - - return aliasMapper.toDomainEntity(aliasJpaEntity); - } -} diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasJpaRepository.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasJpaRepository.java index 130df4c2c..2ba8d51ec 100644 --- a/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasJpaRepository.java +++ b/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasJpaRepository.java @@ -3,5 +3,9 @@ import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface AliasJpaRepository extends JpaRepository, AliasQueryRepository { + + Optional findByValue(String value); } \ No newline at end of file diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryPersistenceAdapter.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryPersistenceAdapter.java deleted file mode 100644 index 0fe88291d..000000000 --- a/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryPersistenceAdapter.java +++ /dev/null @@ -1,18 +0,0 @@ -package konkuk.thip.user.adapter.out.persistence; - -import konkuk.thip.user.application.port.in.dto.UserViewAliasChoiceResult; -import konkuk.thip.user.application.port.out.AliasQueryPort; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class AliasQueryPersistenceAdapter implements AliasQueryPort { - - private final AliasJpaRepository aliasJpaRepository; - - @Override - public UserViewAliasChoiceResult getAllAliasesAndCategories() { - return aliasJpaRepository.getAllAliasesAndCategories(); - } -} diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryRepositoryImpl.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryRepositoryImpl.java index a3791cd11..0482b2074 100644 --- a/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryRepositoryImpl.java +++ b/src/main/java/konkuk/thip/user/adapter/out/persistence/AliasQueryRepositoryImpl.java @@ -24,7 +24,6 @@ public UserViewAliasChoiceResult getAllAliasesAndCategories() { List aliasChoices = jpaQueryFactory .select(Projections.constructor( UserViewAliasChoiceResult.AliasChoice.class, - alias.aliasId, alias.value, category.value, alias.imageUrl, diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/UserCommandPersistenceAdapter.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/UserCommandPersistenceAdapter.java index eda795f14..6909bf929 100644 --- a/src/main/java/konkuk/thip/user/adapter/out/persistence/UserCommandPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/user/adapter/out/persistence/UserCommandPersistenceAdapter.java @@ -23,7 +23,7 @@ public class UserCommandPersistenceAdapter implements UserCommandPort { @Override public Long save(User user) { - AliasJpaEntity aliasJpaEntity = aliasJpaRepository.findById(user.getAliasId()).orElseThrow( + AliasJpaEntity aliasJpaEntity = aliasJpaRepository.findByValue(user.getAlias().getValue()).orElseThrow( () -> new EntityNotFoundException(ALIAS_NOT_FOUND)); UserJpaEntity userJpaEntity = userMapper.toJpaEntity(user, aliasJpaEntity); diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/UserQueryPersistenceAdapter.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/UserQueryPersistenceAdapter.java index 586fc5942..b28f73e4a 100644 --- a/src/main/java/konkuk/thip/user/adapter/out/persistence/UserQueryPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/user/adapter/out/persistence/UserQueryPersistenceAdapter.java @@ -1,6 +1,7 @@ package konkuk.thip.user.adapter.out.persistence; import konkuk.thip.user.adapter.out.mapper.UserMapper; +import konkuk.thip.user.application.port.in.dto.UserViewAliasChoiceResult; import konkuk.thip.user.application.port.out.UserQueryPort; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; @@ -12,6 +13,7 @@ public class UserQueryPersistenceAdapter implements UserQueryPort { private final UserJpaRepository userJpaRepository; + private final AliasJpaRepository aliasJpaRepository; private final UserMapper userMapper; @Override @@ -23,4 +25,9 @@ public boolean existsByNickname(String nickname) { public Set findUserIdsParticipatedInRoomsByBookId(Long bookId) { return userJpaRepository.findUserIdsByBookId(bookId); } + + @Override + public UserViewAliasChoiceResult getAllAliasesAndCategories() { + return aliasJpaRepository.getAllAliasesAndCategories(); + } } \ No newline at end of file diff --git a/src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupCommand.java b/src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupCommand.java index 0b23769e8..450a5a2e0 100644 --- a/src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupCommand.java +++ b/src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupCommand.java @@ -4,7 +4,7 @@ @Builder public record UserSignupCommand( - Long aliasId, + String aliasName, String nickname, String oauth2Id ) {} diff --git a/src/main/java/konkuk/thip/user/application/port/in/dto/UserViewAliasChoiceResult.java b/src/main/java/konkuk/thip/user/application/port/in/dto/UserViewAliasChoiceResult.java index b60820376..60d70366f 100644 --- a/src/main/java/konkuk/thip/user/application/port/in/dto/UserViewAliasChoiceResult.java +++ b/src/main/java/konkuk/thip/user/application/port/in/dto/UserViewAliasChoiceResult.java @@ -5,7 +5,6 @@ public record UserViewAliasChoiceResult(List aliasChoices) { public record AliasChoice( - Long aliasId, String aliasName, String categoryName, String imageUrl, diff --git a/src/main/java/konkuk/thip/user/application/port/out/AliasCommandPort.java b/src/main/java/konkuk/thip/user/application/port/out/AliasCommandPort.java deleted file mode 100644 index be3bbf093..000000000 --- a/src/main/java/konkuk/thip/user/application/port/out/AliasCommandPort.java +++ /dev/null @@ -1,8 +0,0 @@ -package konkuk.thip.user.application.port.out; - -import konkuk.thip.user.domain.Alias; - -public interface AliasCommandPort { - - Alias findById(Long aliasId); -} diff --git a/src/main/java/konkuk/thip/user/application/port/out/AliasQueryPort.java b/src/main/java/konkuk/thip/user/application/port/out/AliasQueryPort.java deleted file mode 100644 index 064428123..000000000 --- a/src/main/java/konkuk/thip/user/application/port/out/AliasQueryPort.java +++ /dev/null @@ -1,8 +0,0 @@ -package konkuk.thip.user.application.port.out; - -import konkuk.thip.user.application.port.in.dto.UserViewAliasChoiceResult; - -public interface AliasQueryPort { - - UserViewAliasChoiceResult getAllAliasesAndCategories(); -} diff --git a/src/main/java/konkuk/thip/user/application/port/out/UserQueryPort.java b/src/main/java/konkuk/thip/user/application/port/out/UserQueryPort.java index 3a6a55d28..5b71774eb 100644 --- a/src/main/java/konkuk/thip/user/application/port/out/UserQueryPort.java +++ b/src/main/java/konkuk/thip/user/application/port/out/UserQueryPort.java @@ -1,8 +1,12 @@ package konkuk.thip.user.application.port.out; +import konkuk.thip.user.application.port.in.dto.UserViewAliasChoiceResult; + import java.util.Set; public interface UserQueryPort { boolean existsByNickname(String nickname); Set findUserIdsParticipatedInRoomsByBookId(Long bookId); + + UserViewAliasChoiceResult getAllAliasesAndCategories(); } diff --git a/src/main/java/konkuk/thip/user/application/service/UserSignupService.java b/src/main/java/konkuk/thip/user/application/service/UserSignupService.java index 478ed31e4..105e49eb9 100644 --- a/src/main/java/konkuk/thip/user/application/service/UserSignupService.java +++ b/src/main/java/konkuk/thip/user/application/service/UserSignupService.java @@ -2,7 +2,6 @@ import konkuk.thip.user.application.port.in.UserSignupUseCase; import konkuk.thip.user.application.port.in.dto.UserSignupCommand; -import konkuk.thip.user.application.port.out.AliasCommandPort; import konkuk.thip.user.application.port.out.UserCommandPort; import konkuk.thip.user.domain.Alias; import konkuk.thip.user.domain.User; @@ -18,14 +17,13 @@ public class UserSignupService implements UserSignupUseCase { private final UserCommandPort userCommandPort; - private final AliasCommandPort aliasCommandPort; @Override @Transactional public Long signup(UserSignupCommand command) { - Alias alias = aliasCommandPort.findById(command.aliasId()); + Alias alias = Alias.from(command.aliasName()); User user = User.withoutId( - command.nickname(), alias.getImageUrl(), USER.getType(), alias.getId(), command.oauth2Id() + command.nickname(), USER.getType(), command.oauth2Id(), alias ); return userCommandPort.save(user); diff --git a/src/main/java/konkuk/thip/user/application/service/UserViewAliasChoiceService.java b/src/main/java/konkuk/thip/user/application/service/UserViewAliasChoiceService.java index b04dd2df6..50c64cd2f 100644 --- a/src/main/java/konkuk/thip/user/application/service/UserViewAliasChoiceService.java +++ b/src/main/java/konkuk/thip/user/application/service/UserViewAliasChoiceService.java @@ -2,7 +2,7 @@ import konkuk.thip.user.application.port.in.UserViewAliasChoiceUseCase; import konkuk.thip.user.application.port.in.dto.UserViewAliasChoiceResult; -import konkuk.thip.user.application.port.out.AliasQueryPort; +import konkuk.thip.user.application.port.out.UserQueryPort; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -10,10 +10,10 @@ @RequiredArgsConstructor public class UserViewAliasChoiceService implements UserViewAliasChoiceUseCase { - private final AliasQueryPort aliasQueryPort; + private final UserQueryPort userQueryPort; @Override public UserViewAliasChoiceResult getAllAliasesAndCategories() { - return aliasQueryPort.getAllAliasesAndCategories(); + return userQueryPort.getAllAliasesAndCategories(); } } diff --git a/src/main/java/konkuk/thip/user/domain/Alias.java b/src/main/java/konkuk/thip/user/domain/Alias.java index 76e8c599d..992b7994e 100644 --- a/src/main/java/konkuk/thip/user/domain/Alias.java +++ b/src/main/java/konkuk/thip/user/domain/Alias.java @@ -1,18 +1,29 @@ package konkuk.thip.user.domain; -import konkuk.thip.common.entity.BaseDomainEntity; +import konkuk.thip.common.exception.InvalidStateException; +import konkuk.thip.common.exception.code.ErrorCode; import lombok.Getter; -import lombok.experimental.SuperBuilder; +import lombok.RequiredArgsConstructor; @Getter -@SuperBuilder -public class Alias extends BaseDomainEntity { +@RequiredArgsConstructor +public enum Alias { + WRITER("문학가", "문학_color", "문학_image"), + SCIENTIST("과학자", "과학_color", "과학_image"), + SOCIOLOGIST("사회학자", "사회과학_color", "사회과학_image"), + ARTIST("예술가", "예술_color", "예술_image"), + PHILOSOPHER("철학자", "철학_color", "철학_image"); - private Long id; + private final String value; + private final String color; + private final String imageUrl; - private String value; - - private String imageUrl; - - private String color; + public static Alias from(String value) { + for (Alias alias : Alias.values()) { + if (alias.value.equals(value)) { + return alias; + } + } + throw new InvalidStateException(ErrorCode.ALIAS_NAME_NOT_MATCH); + } } diff --git a/src/main/java/konkuk/thip/user/domain/User.java b/src/main/java/konkuk/thip/user/domain/User.java index b19513dd7..3de8ab730 100644 --- a/src/main/java/konkuk/thip/user/domain/User.java +++ b/src/main/java/konkuk/thip/user/domain/User.java @@ -12,22 +12,19 @@ public class User extends BaseDomainEntity { private String nickname; - private String imageUrl; - private String userRole; - private Long aliasId; - private String oauth2Id; - public static User withoutId(String nickname, String imageUrl, String userRole, Long aliasId, String oauth2Id) { + private Alias alias; + + public static User withoutId(String nickname, String userRole, String oauth2Id, Alias alias) { return User.builder() .id(null) .nickname(nickname) - .imageUrl(imageUrl) .userRole(userRole) - .aliasId(aliasId) .oauth2Id(oauth2Id) + .alias(alias) .build(); } diff --git a/src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java b/src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java index 204ac4cde..942bb370b 100644 --- a/src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java +++ b/src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java @@ -6,6 +6,7 @@ import konkuk.thip.book.adapter.out.persistence.BookJpaRepository; import konkuk.thip.common.exception.code.ErrorCode; import konkuk.thip.common.security.util.JwtUtil; +import konkuk.thip.common.util.TestEntityFactory; import konkuk.thip.saved.adapter.out.jpa.SavedBookJpaEntity; import konkuk.thip.saved.adapter.out.persistence.SavedBookJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; @@ -66,11 +67,7 @@ class BookChangeSavedControllerTest { @BeforeEach void setUp() { - AliasJpaEntity alias = aliasJpaRepository.save(AliasJpaEntity.builder() - .value("책벌레") - .color("blue") - .imageUrl("http://image.url") - .build()); + AliasJpaEntity alias = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias()); UserJpaEntity user = userJpaRepository.save(UserJpaEntity.builder() .oauth2Id("kakao_432708231") diff --git a/src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchControllerTest.java b/src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchControllerTest.java index b86063d35..6709b9e51 100644 --- a/src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchControllerTest.java +++ b/src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchControllerTest.java @@ -4,6 +4,7 @@ import konkuk.thip.book.application.service.BookSearchService; import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import konkuk.thip.common.security.util.JwtUtil; +import konkuk.thip.common.util.TestEntityFactory; import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; import konkuk.thip.room.adapter.out.persistence.CategoryJpaRepository; import konkuk.thip.user.adapter.out.jpa.*; @@ -62,11 +63,7 @@ class BookDetailSearchControllerTest { @BeforeEach void setup() { - AliasJpaEntity alias = aliasJpaRepository.save(AliasJpaEntity.builder() - .value("책벌레") - .color("blue") - .imageUrl("http://image.url") - .build()); + AliasJpaEntity alias = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias()); UserJpaEntity user = userJpaRepository.save(UserJpaEntity.builder() .oauth2Id("kakao_432708231") @@ -87,11 +84,7 @@ void setup() { .description("한강의 소설") .build()); - - CategoryJpaEntity category = categoryJpaRepository.save(CategoryJpaEntity.builder() - .value("소설") - .aliasForCategoryJpaEntity(alias) - .build()); + CategoryJpaEntity category = categoryJpaRepository.save(TestEntityFactory.createLiteratureCategory(alias)); RoomJpaEntity room = roomJpaRepository.save(RoomJpaEntity.builder() .title("한강 독서모임") diff --git a/src/test/java/konkuk/thip/book/adapter/in/web/BookMostSearchedBooksControllerTest.java b/src/test/java/konkuk/thip/book/adapter/in/web/BookMostSearchedBooksControllerTest.java index d90a218ad..cbb4e82fe 100644 --- a/src/test/java/konkuk/thip/book/adapter/in/web/BookMostSearchedBooksControllerTest.java +++ b/src/test/java/konkuk/thip/book/adapter/in/web/BookMostSearchedBooksControllerTest.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import konkuk.thip.book.application.port.in.dto.BookMostSearchResult; +import konkuk.thip.common.util.TestEntityFactory; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserRole; @@ -62,12 +63,7 @@ class BookMostSearchedBooksControllerTest { @BeforeEach void setUp() { - - AliasJpaEntity alias = aliasJpaRepository.save(AliasJpaEntity.builder() - .value("책벌레") - .color("blue") - .imageUrl("http://image.url") - .build()); + AliasJpaEntity alias = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias()); UserJpaEntity user = userJpaRepository.save(UserJpaEntity.builder() .oauth2Id("kakao_432708231") @@ -76,7 +72,6 @@ void setUp() { .role(UserRole.USER) .aliasForUserJpaEntity(alias) .build()); - } @AfterEach diff --git a/src/test/java/konkuk/thip/book/adapter/in/web/BookQueryControllerTest.java b/src/test/java/konkuk/thip/book/adapter/in/web/BookQueryControllerTest.java index 6aba36187..7e3eb636b 100644 --- a/src/test/java/konkuk/thip/book/adapter/in/web/BookQueryControllerTest.java +++ b/src/test/java/konkuk/thip/book/adapter/in/web/BookQueryControllerTest.java @@ -2,6 +2,7 @@ import konkuk.thip.common.exception.code.ErrorCode; import konkuk.thip.common.security.util.JwtUtil; +import konkuk.thip.common.util.TestEntityFactory; import konkuk.thip.recentSearch.adapter.out.jpa.RecentSearchJpaEntity; import konkuk.thip.recentSearch.adapter.out.jpa.SearchType; import konkuk.thip.recentSearch.adapter.out.persistence.RecentSearchJpaRepository; @@ -51,12 +52,7 @@ class BookQueryControllerTest { @BeforeEach void setUp() { - - AliasJpaEntity alias = aliasJpaRepository.save(AliasJpaEntity.builder() - .value("책벌레") - .color("blue") - .imageUrl("http://image.url") - .build()); + AliasJpaEntity alias = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias()); UserJpaEntity user = userJpaRepository.save(UserJpaEntity.builder() .oauth2Id("kakao_432708231") diff --git a/src/test/java/konkuk/thip/common/util/TestEntityFactory.java b/src/test/java/konkuk/thip/common/util/TestEntityFactory.java index af1d4f988..2df450014 100644 --- a/src/test/java/konkuk/thip/common/util/TestEntityFactory.java +++ b/src/test/java/konkuk/thip/common/util/TestEntityFactory.java @@ -15,11 +15,37 @@ public class TestEntityFactory { - public static AliasJpaEntity createAlias() { - return AliasJpaEntity.builder() - .value("칭호") - .imageUrl("test-image-url") - .color("red") + /** + * 유효한 Jpa entity를 만들어주는 Factory + */ + + public static AliasJpaEntity createLiteratureAlias() { + return AliasJpaEntity.builder() // 실제 존재하는 값으로 + .value("문학가") + .imageUrl("문학_image") + .color("문학_color") + .build(); + } + + public static CategoryJpaEntity createLiteratureCategory(AliasJpaEntity alias) { + return CategoryJpaEntity.builder() // 실제 존재하는 값으로 + .value("문학") + .aliasForCategoryJpaEntity(alias) + .build(); + } + + public static AliasJpaEntity createScienceAlias() { + return AliasJpaEntity.builder() // 실제 존재하는 값으로 + .value("과학자") + .imageUrl("과학_image") + .color("과학_color") + .build(); + } + + public static CategoryJpaEntity createScienceCategory(AliasJpaEntity alias) { + return CategoryJpaEntity.builder() // 실제 존재하는 값으로 + .value("과학/IT") + .aliasForCategoryJpaEntity(alias) .build(); } @@ -46,13 +72,6 @@ public static BookJpaEntity createBook() { .build(); } - public static CategoryJpaEntity createCategory(AliasJpaEntity alias) { - return CategoryJpaEntity.builder() - .value("과학/IT") // 실제 존재하는 값 - .aliasForCategoryJpaEntity(alias) - .build(); - } - public static RoomJpaEntity createRoom(BookJpaEntity book, CategoryJpaEntity category) { return RoomJpaEntity.builder() .title("방이름") diff --git a/src/test/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntityTest.java b/src/test/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntityTest.java index 03d6484a1..cd4c013df 100644 --- a/src/test/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntityTest.java +++ b/src/test/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntityTest.java @@ -43,7 +43,7 @@ class FeedJpaEntityTest { @DisplayName("FeedJpaEntity 저장 및 조회 테스트") void saveAndFindFeed() { // given - AliasJpaEntity alias = aliasRepository.save(TestEntityFactory.createAlias()); + AliasJpaEntity alias = aliasRepository.save(TestEntityFactory.createLiteratureAlias()); UserJpaEntity user = userRepository.save(TestEntityFactory.createUser(alias)); BookJpaEntity book = bookRepository.save(TestEntityFactory.createBook()); diff --git a/src/test/java/konkuk/thip/record/adapter/in/web/RecordCreateControllerTest.java b/src/test/java/konkuk/thip/record/adapter/in/web/RecordCreateControllerTest.java index 04279b9ce..7538ea3fb 100644 --- a/src/test/java/konkuk/thip/record/adapter/in/web/RecordCreateControllerTest.java +++ b/src/test/java/konkuk/thip/record/adapter/in/web/RecordCreateControllerTest.java @@ -81,13 +81,13 @@ void tearDown() { } private void saveUserAndRoom() { - AliasJpaEntity alias = aliasJpaRepository.save(TestEntityFactory.createAlias()); + AliasJpaEntity alias = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias()); UserJpaEntity user = userJpaRepository.save(TestEntityFactory.createUser(alias)); BookJpaEntity book = bookJpaRepository.save(TestEntityFactory.createBook()); - CategoryJpaEntity category = categoryJpaRepository.save(TestEntityFactory.createCategory(alias)); + CategoryJpaEntity category = categoryJpaRepository.save(TestEntityFactory.createLiteratureCategory(alias)); RoomJpaEntity room = roomJpaRepository.save(TestEntityFactory.createRoom(book, category)); diff --git a/src/test/java/konkuk/thip/record/adapter/in/web/RecordQueryControllerTest.java b/src/test/java/konkuk/thip/record/adapter/in/web/RecordQueryControllerTest.java index 434b77cde..43334110e 100644 --- a/src/test/java/konkuk/thip/record/adapter/in/web/RecordQueryControllerTest.java +++ b/src/test/java/konkuk/thip/record/adapter/in/web/RecordQueryControllerTest.java @@ -5,6 +5,7 @@ import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import konkuk.thip.book.adapter.out.persistence.BookJpaRepository; import konkuk.thip.common.util.DateUtil; +import konkuk.thip.common.util.TestEntityFactory; import konkuk.thip.record.adapter.out.jpa.RecordJpaEntity; import konkuk.thip.record.adapter.out.persistence.RecordJpaRepository; import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; @@ -71,11 +72,7 @@ void tearDown() { @DisplayName("기록장 조회 시 record와 vote 모두 조회") void record_with_vote_response_success() throws Exception { // given - AliasJpaEntity alias = aliasJpaRepository.save(AliasJpaEntity.builder() - .value("별명") - .color("blue") - .imageUrl("http://alias.img") - .build()); + AliasJpaEntity alias = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias()); UserJpaEntity user = userJpaRepository.save(UserJpaEntity.builder() .oauth2Id("kakao_123") @@ -96,10 +93,7 @@ void record_with_vote_response_success() throws Exception { .bestSeller(false) .build()); - CategoryJpaEntity category = categoryJpaRepository.save(CategoryJpaEntity.builder() - .value("소설") - .aliasForCategoryJpaEntity(alias) - .build()); + CategoryJpaEntity category = categoryJpaRepository.save(TestEntityFactory.createLiteratureCategory(alias)); RoomJpaEntity room = roomJpaRepository.save(RoomJpaEntity.builder() .title("방 제목") diff --git a/src/test/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImplTest.java b/src/test/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImplTest.java index de2ab3616..62f26ca83 100644 --- a/src/test/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImplTest.java +++ b/src/test/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImplTest.java @@ -42,7 +42,7 @@ class RecordQueryRepositoryImplTest { @BeforeEach void setUp() { - AliasJpaEntity alias = TestEntityFactory.createAlias(); + AliasJpaEntity alias = TestEntityFactory.createLiteratureAlias(); em.persist(alias); user1 = TestEntityFactory.createUser(alias); @@ -53,7 +53,7 @@ void setUp() { BookJpaEntity book = TestEntityFactory.createBook(); em.persist(book); - CategoryJpaEntity category = TestEntityFactory.createCategory(alias); + CategoryJpaEntity category = TestEntityFactory.createLiteratureCategory(alias); em.persist(category); room = TestEntityFactory.createRoom(book, category); diff --git a/src/test/java/konkuk/thip/room/adapter/in/web/RoomCreateAPITest.java b/src/test/java/konkuk/thip/room/adapter/in/web/RoomCreateAPITest.java index 67471027b..452fef6c5 100644 --- a/src/test/java/konkuk/thip/room/adapter/in/web/RoomCreateAPITest.java +++ b/src/test/java/konkuk/thip/room/adapter/in/web/RoomCreateAPITest.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import konkuk.thip.book.adapter.out.persistence.BookJpaRepository; +import konkuk.thip.common.util.TestEntityFactory; import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; import konkuk.thip.room.adapter.out.jpa.RoomJpaEntity; import konkuk.thip.room.adapter.out.persistence.CategoryJpaRepository; @@ -72,11 +73,7 @@ void tearDown() { } private void saveUserAndCategory() { - AliasJpaEntity alias = aliasJpaRepository.save(AliasJpaEntity.builder() - .value("책벌레") - .color("blue") - .imageUrl("http://image.url") - .build()); + AliasJpaEntity alias = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias()); userJpaRepository.save(UserJpaEntity.builder() .oauth2Id("kakao_432708231") @@ -86,10 +83,7 @@ private void saveUserAndCategory() { .aliasForUserJpaEntity(alias) .build()); - categoryJpaRepository.save(CategoryJpaEntity.builder() - .value("과학/IT") // 실제 카테고리 값 - .aliasForCategoryJpaEntity(alias) - .build()); + CategoryJpaEntity category = categoryJpaRepository.save(TestEntityFactory.createLiteratureCategory(alias)); } private void saveBookWithPageCount() { @@ -121,11 +115,11 @@ private void saveBookWithoutPageCount() { private Map buildRoomCreateRequest() { Map request = new HashMap<>(); request.put("isbn", "9788954682152"); - request.put("category", "과학/IT"); // 실제 카테고리 값 + request.put("category", "문학"); // 실제 카테고리 값 request.put("roomName", "방이름"); request.put("description", "방설명"); request.put("progressStartDate", LocalDate.now().plusDays(1).format(DateTimeFormatter.ofPattern("yyyy.MM.dd"))); - request.put("progressEndDate", LocalDate.now().plusDays(30).format(DateTimeFormatter.ofPattern("yyyy.MM.dd"))); + request.put("progressEndDate", LocalDate.now().plusDays(10).format(DateTimeFormatter.ofPattern("yyyy.MM.dd"))); request.put("recruitCount", 3); request.put("password", null); request.put("isPublic", true); diff --git a/src/test/java/konkuk/thip/room/adapter/in/web/RoomSearchApiTest.java b/src/test/java/konkuk/thip/room/adapter/in/web/RoomSearchApiTest.java index bba425dba..da46a3141 100644 --- a/src/test/java/konkuk/thip/room/adapter/in/web/RoomSearchApiTest.java +++ b/src/test/java/konkuk/thip/room/adapter/in/web/RoomSearchApiTest.java @@ -1,8 +1,8 @@ package konkuk.thip.room.adapter.in.web; -import com.fasterxml.jackson.databind.ObjectMapper; import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import konkuk.thip.book.adapter.out.persistence.BookJpaRepository; +import konkuk.thip.common.util.TestEntityFactory; import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; import konkuk.thip.room.adapter.out.jpa.RoomJpaEntity; import konkuk.thip.room.adapter.out.persistence.CategoryJpaRepository; @@ -68,12 +68,8 @@ void tearDown() { aliasJpaRepository.deleteAll(); } - private RoomJpaEntity saveRoom(String categoryValue, String bookTitle, String isbn, String roomName, LocalDate startDate, int recruitCount) { - AliasJpaEntity alias = aliasJpaRepository.save(AliasJpaEntity.builder() - .value("소설-칭호") - .color("blue") - .imageUrl("http://image.url") - .build()); + private RoomJpaEntity saveScienceRoom(String bookTitle, String isbn, String roomName, LocalDate startDate, int recruitCount) { + AliasJpaEntity alias = aliasJpaRepository.save(TestEntityFactory.createScienceAlias()); BookJpaEntity book = bookJpaRepository.save(BookJpaEntity.builder() .title(bookTitle) @@ -86,10 +82,36 @@ private RoomJpaEntity saveRoom(String categoryValue, String bookTitle, String is .description("한강의 소설") .build()); - CategoryJpaEntity category = categoryJpaRepository.save(CategoryJpaEntity.builder() - .value(categoryValue) - .aliasForCategoryJpaEntity(alias) + CategoryJpaEntity category = categoryJpaRepository.save(TestEntityFactory.createScienceCategory(alias)); + + return roomJpaRepository.save(RoomJpaEntity.builder() + .title(roomName) + .description("한강 작품 읽기 모임") + .isPublic(true) + .roomPercentage(0.0) + .startDate(startDate) + .endDate(LocalDate.now().plusDays(30)) + .recruitCount(recruitCount) + .bookJpaEntity(book) + .categoryJpaEntity(category) .build()); + } + + private RoomJpaEntity saveLiteratureRoom(String bookTitle, String isbn, String roomName, LocalDate startDate, int recruitCount) { + AliasJpaEntity alias = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias()); + + BookJpaEntity book = bookJpaRepository.save(BookJpaEntity.builder() + .title(bookTitle) + .isbn(isbn) + .authorName("한강") + .bestSeller(false) + .publisher("문학동네") + .imageUrl("https://image1.jpg") + .pageCount(300) + .description("한강의 소설") + .build()); + + CategoryJpaEntity category = categoryJpaRepository.save(TestEntityFactory.createLiteratureCategory(alias)); return roomJpaRepository.save(RoomJpaEntity.builder() .title(roomName) @@ -105,11 +127,7 @@ private RoomJpaEntity saveRoom(String categoryValue, String bookTitle, String is } private void saveUsersToRoom(RoomJpaEntity roomJpaEntity, int count) { - AliasJpaEntity alias = aliasJpaRepository.save(AliasJpaEntity.builder() - .value("소설-칭호") - .color("blue") - .imageUrl("http://image.url") - .build()); + AliasJpaEntity alias = aliasJpaRepository.save(TestEntityFactory.createScienceAlias()); // User 리스트 생성 및 저장 List users = IntStream.rangeClosed(1, count) @@ -140,22 +158,22 @@ private void saveUsersToRoom(RoomJpaEntity roomJpaEntity, int count) { @DisplayName("keyword = [과학], 카테고리 선택 X, 정렬 = [마감임박순] 일 경우, 방이름 or 책제목에 '과학'이 포함된 방 검색 결과가 마감임박순으로 반환된다.") void search_keyword_and_sort_deadline() throws Exception { //given - RoomJpaEntity science_room_1 = saveRoom("과학/IT", "과학-책", "isbn1", "과학-방-1일뒤-활동시작", LocalDate.now().plusDays(1), 10); + RoomJpaEntity science_room_1 = saveScienceRoom("과학-책", "isbn1", "과학-방-1일뒤-활동시작", LocalDate.now().plusDays(1), 10); saveUsersToRoom(science_room_1, 4); - RoomJpaEntity science_room_2 = saveRoom("과학/IT", "과학-책", "isbn2", "방이름입니다", LocalDate.now().plusDays(1), 10); + RoomJpaEntity science_room_2 = saveScienceRoom("과학-책", "isbn2", "방이름입니다", LocalDate.now().plusDays(1), 10); saveUsersToRoom(science_room_2, 5); - RoomJpaEntity science_room_3 = saveRoom("과학/IT", "과학-책", "isbn3", "무슨방일까요??", LocalDate.now().plusDays(5), 8); + RoomJpaEntity science_room_3 = saveScienceRoom("과학-책", "isbn3", "무슨방일까요??", LocalDate.now().plusDays(5), 8); saveUsersToRoom(science_room_3, 2); - RoomJpaEntity science_room_4 = saveRoom("과학/IT", "과학-책", "isbn4", "과학-방-5일뒤-활동시작", LocalDate.now().plusDays(5), 8); + RoomJpaEntity science_room_4 = saveScienceRoom("과학-책", "isbn4", "과학-방-5일뒤-활동시작", LocalDate.now().plusDays(5), 8); saveUsersToRoom(science_room_4, 1); - RoomJpaEntity room_3 = saveRoom("문학", "문학-책", "isbn5", "방제목에-과학-포함된-문학방", LocalDate.now().plusDays(10), 8); + RoomJpaEntity room_3 = saveLiteratureRoom("문학-책", "isbn5", "방제목에-과학-포함된-문학방", LocalDate.now().plusDays(10), 8); saveUsersToRoom(room_3, 6); - RoomJpaEntity recruit_expired_room_4 = saveRoom("과학/IT", "과학-책", "isbn6", "모집기한-지난-과학방", LocalDate.now().minusDays(1), 8); + RoomJpaEntity recruit_expired_room_4 = saveScienceRoom("과학-책", "isbn6", "모집기한-지난-과학방", LocalDate.now().minusDays(1), 8); saveUsersToRoom(recruit_expired_room_4, 6); //when @@ -189,22 +207,22 @@ void search_keyword_and_sort_deadline() throws Exception { @DisplayName("keyword = [과학], 카테고리 선택 X, 정렬 = [인기순] 일 경우, 방이름 or 책제목에 '과학'이 포함된 방 검색 결과가 인기순(= 현재까지 모집된 인원 많은 순)으로 반환된다.") void search_keyword_and_sort_member_count() throws Exception { //given - RoomJpaEntity science_room_1 = saveRoom("과학/IT", "과학-책", "isbn1", "과학-방-1일뒤-활동시작", LocalDate.now().plusDays(1), 10); + RoomJpaEntity science_room_1 = saveScienceRoom("과학-책", "isbn1", "과학-방-1일뒤-활동시작", LocalDate.now().plusDays(1), 10); saveUsersToRoom(science_room_1, 4); - RoomJpaEntity science_room_2 = saveRoom("과학/IT", "과학-책", "isbn2", "방이름입니다", LocalDate.now().plusDays(1), 10); + RoomJpaEntity science_room_2 = saveScienceRoom("과학-책", "isbn2", "방이름입니다", LocalDate.now().plusDays(1), 10); saveUsersToRoom(science_room_2, 5); - RoomJpaEntity science_room_3 = saveRoom("과학/IT", "과학-책", "isbn3", "무슨방일까요??", LocalDate.now().plusDays(5), 8); + RoomJpaEntity science_room_3 = saveScienceRoom("과학-책", "isbn3", "무슨방일까요??", LocalDate.now().plusDays(5), 8); saveUsersToRoom(science_room_3, 2); - RoomJpaEntity science_room_4 = saveRoom("과학/IT", "과학-책", "isbn4", "과학-방-5일뒤-활동시작", LocalDate.now().plusDays(5), 8); + RoomJpaEntity science_room_4 = saveScienceRoom("과학-책", "isbn4", "과학-방-5일뒤-활동시작", LocalDate.now().plusDays(5), 8); saveUsersToRoom(science_room_4, 1); - RoomJpaEntity room_3 = saveRoom("문학", "문학-책", "isbn5", "방제목에-과학-포함된-문학방", LocalDate.now().plusDays(10), 8); + RoomJpaEntity room_3 = saveLiteratureRoom("문학-책", "isbn5", "방제목에-과학-포함된-문학방", LocalDate.now().plusDays(10), 8); saveUsersToRoom(room_3, 6); - RoomJpaEntity recruit_expired_room_4 = saveRoom("과학/IT", "과학-책", "isbn6", "모집기한-지난-과학방", LocalDate.now().minusDays(1), 8); + RoomJpaEntity recruit_expired_room_4 = saveScienceRoom("과학-책", "isbn6", "모집기한-지난-과학방", LocalDate.now().minusDays(1), 8); saveUsersToRoom(recruit_expired_room_4, 6); //when @@ -247,16 +265,16 @@ void search_keyword_and_sort_member_count() throws Exception { @DisplayName("keyword 입력 x, 카테고리 = [과학/IT], 정렬 = [마감임박순] 일 경우, [과학/IT] 카테고리에 속하는 방 검색 결과가 반환된다.") void search_category_and_sort_deadline() throws Exception { //given - RoomJpaEntity science_room_1 = saveRoom("과학/IT", "과학-책", "isbn1", "과학-방-1일뒤-활동시작", LocalDate.now().plusDays(1), 10); + RoomJpaEntity science_room_1 = saveScienceRoom("과학-책", "isbn1", "과학-방-1일뒤-활동시작", LocalDate.now().plusDays(1), 10); saveUsersToRoom(science_room_1, 4); - RoomJpaEntity science_room_3 = saveRoom("과학/IT", "과학-책", "isbn3", "무슨방일까요??", LocalDate.now().plusDays(5), 8); + RoomJpaEntity science_room_3 = saveScienceRoom("과학-책", "isbn3", "무슨방일까요??", LocalDate.now().plusDays(5), 8); saveUsersToRoom(science_room_3, 2); - RoomJpaEntity room_3 = saveRoom("문학", "문학-책", "isbn5", "방제목에-과학-포함된-문학방", LocalDate.now().plusDays(10), 8); + RoomJpaEntity room_3 = saveLiteratureRoom("문학-책", "isbn5", "방제목에-과학-포함된-문학방", LocalDate.now().plusDays(10), 8); saveUsersToRoom(room_3, 6); - RoomJpaEntity recruit_expired_room_4 = saveRoom("과학/IT", "과학-책", "isbn6", "모집기한-지난-과학방", LocalDate.now().minusDays(1), 8); + RoomJpaEntity recruit_expired_room_4 = saveScienceRoom("과학-책", "isbn6", "모집기한-지난-과학방", LocalDate.now().minusDays(1), 8); saveUsersToRoom(recruit_expired_room_4, 6); //when @@ -287,16 +305,16 @@ void search_category_and_sort_deadline() throws Exception { @DisplayName("keyword 입력 x, 카테고리 입력 x, 정렬 = [마감임박순] 일 경우, DB에 존재하는 전체 방 검색 결과가 반환된다.") void search_sort_deadline() throws Exception { //given - RoomJpaEntity science_room_1 = saveRoom("과학/IT", "과학-책", "isbn1", "과학-방-1일뒤-활동시작", LocalDate.now().plusDays(1), 10); + RoomJpaEntity science_room_1 = saveScienceRoom("과학-책", "isbn1", "과학-방-1일뒤-활동시작", LocalDate.now().plusDays(1), 10); saveUsersToRoom(science_room_1, 4); - RoomJpaEntity science_room_3 = saveRoom("과학/IT", "과학-책", "isbn3", "무슨방일까요??", LocalDate.now().plusDays(5), 8); + RoomJpaEntity science_room_3 = saveScienceRoom("과학-책", "isbn3", "무슨방일까요??", LocalDate.now().plusDays(5), 8); saveUsersToRoom(science_room_3, 2); - RoomJpaEntity room_3 = saveRoom("문학", "문학-책", "isbn5", "방제목에-과학-포함된-문학방", LocalDate.now().plusDays(10), 8); + RoomJpaEntity room_3 = saveLiteratureRoom("문학-책", "isbn5", "방제목에-과학-포함된-문학방", LocalDate.now().plusDays(10), 8); saveUsersToRoom(room_3, 6); - RoomJpaEntity recruit_expired_room_4 = saveRoom("과학/IT", "과학-책", "isbn6", "모집기한-지난-과학방", LocalDate.now().minusDays(1), 8); + RoomJpaEntity recruit_expired_room_4 = saveScienceRoom("과학-책", "isbn6", "모집기한-지난-과학방", LocalDate.now().minusDays(1), 8); saveUsersToRoom(recruit_expired_room_4, 6); //when @@ -327,16 +345,16 @@ void search_sort_deadline() throws Exception { @DisplayName("keyword=[과학], category=[과학/IT], 정렬=[마감임박순] 일 경우, keyword, category 조건을 모두 만족하는 방만 반환된다.") void search_keyword_and_category() throws Exception { // given - RoomJpaEntity science_room_1 = saveRoom("과학/IT", "과학-책", "isbn1", "과학-방-1일뒤-활동시작", LocalDate.now().plusDays(1), 10); + RoomJpaEntity science_room_1 = saveScienceRoom("과학-책", "isbn1", "과학-방-1일뒤-활동시작", LocalDate.now().plusDays(1), 10); saveUsersToRoom(science_room_1, 4); - RoomJpaEntity science_room_3 = saveRoom("과학/IT", "과학-책", "isbn3", "무슨방일까요??", LocalDate.now().plusDays(5), 8); + RoomJpaEntity science_room_3 = saveScienceRoom("과학-책", "isbn3", "무슨방일까요??", LocalDate.now().plusDays(5), 8); saveUsersToRoom(science_room_3, 2); - RoomJpaEntity room_3 = saveRoom("문학", "문학-책", "isbn5", "문학방입니다", LocalDate.now().plusDays(10), 8); + RoomJpaEntity room_3 = saveLiteratureRoom("문학-책", "isbn5", "문학방입니다", LocalDate.now().plusDays(10), 8); saveUsersToRoom(room_3, 6); - RoomJpaEntity recruit_expired_room_4 = saveRoom("과학/IT", "과학-책", "isbn6", "모집기한-지난-과학방", LocalDate.now().minusDays(1), 8); + RoomJpaEntity recruit_expired_room_4 = saveScienceRoom("과학-책", "isbn6", "모집기한-지난-과학방", LocalDate.now().minusDays(1), 8); saveUsersToRoom(recruit_expired_room_4, 6); // when diff --git a/src/test/java/konkuk/thip/room/adapter/in/web/RoomVerifyPasswordAPITest.java b/src/test/java/konkuk/thip/room/adapter/in/web/RoomVerifyPasswordAPITest.java index 3ba330a9e..31cfe0e7c 100644 --- a/src/test/java/konkuk/thip/room/adapter/in/web/RoomVerifyPasswordAPITest.java +++ b/src/test/java/konkuk/thip/room/adapter/in/web/RoomVerifyPasswordAPITest.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import konkuk.thip.book.adapter.out.persistence.BookJpaRepository; +import konkuk.thip.common.util.TestEntityFactory; import konkuk.thip.room.adapter.in.web.request.RoomVerifyPasswordRequest; import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; import konkuk.thip.room.adapter.out.persistence.CategoryJpaRepository; @@ -70,11 +71,7 @@ class RoomVerifyPasswordAPITest { @BeforeEach void setUp() { - AliasJpaEntity alias = aliasJpaRepository.save(AliasJpaEntity.builder() - .value("책벌레") - .color("blue") - .imageUrl("http://image.url") - .build()); + AliasJpaEntity alias = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias()); UserJpaEntity user = userJpaRepository.save(UserJpaEntity.builder() .oauth2Id("kakao_432708231") @@ -95,16 +92,13 @@ void setUp() { .build()); userId = user.getUserId(); - CategoryJpaEntity categoryJpaEntity = categoryJpaRepository.save(CategoryJpaEntity.builder() - .value("과학/IT") - .aliasForCategoryJpaEntity(alias) - .build()); + CategoryJpaEntity category = categoryJpaRepository.save(TestEntityFactory.createLiteratureCategory(alias)); // 비공개 방 저장 (비밀번호: 1234) RoomJpaEntity privateRoom = roomJpaRepository.save( RoomJpaEntity.builder() .bookJpaEntity(book) - .categoryJpaEntity(categoryJpaEntity) + .categoryJpaEntity(category) .title("비공개방") .description("비공개방입니다") .isPublic(false) @@ -120,7 +114,7 @@ void setUp() { RoomJpaEntity publicRoom = roomJpaRepository.save( RoomJpaEntity.builder() .bookJpaEntity(book) - .categoryJpaEntity(categoryJpaEntity) + .categoryJpaEntity(category) .title("공개방") .description("공개방입니다") .isPublic(true) diff --git a/src/test/java/konkuk/thip/room/adapter/out/jpa/RecordJpaEntityTest.java b/src/test/java/konkuk/thip/room/adapter/out/jpa/RecordJpaEntityTest.java index 0d60b0088..d75ae8316 100644 --- a/src/test/java/konkuk/thip/room/adapter/out/jpa/RecordJpaEntityTest.java +++ b/src/test/java/konkuk/thip/room/adapter/out/jpa/RecordJpaEntityTest.java @@ -52,10 +52,10 @@ class RecordJpaEntityTest { @DisplayName("RecordJpaEntity 저장 및 조회 테스트") void saveAndFindRecord() { // given - AliasJpaEntity alias = aliasRepository.save(TestEntityFactory.createAlias()); + AliasJpaEntity alias = aliasRepository.save(TestEntityFactory.createLiteratureAlias()); UserJpaEntity user = userRepository.save(TestEntityFactory.createUser(alias)); BookJpaEntity book = bookRepository.save(TestEntityFactory.createBook()); - CategoryJpaEntity category = categoryRepository.save(TestEntityFactory.createCategory(alias)); + CategoryJpaEntity category = categoryRepository.save(TestEntityFactory.createLiteratureCategory(alias)); RoomJpaEntity room = roomRepository.save(TestEntityFactory.createRoom(book, category)); RecordJpaEntity record = recordRepository.save(TestEntityFactory.createRecord(user, room)); diff --git a/src/test/java/konkuk/thip/room/adapter/out/jpa/RoomJpaEntityTest.java b/src/test/java/konkuk/thip/room/adapter/out/jpa/RoomJpaEntityTest.java index 3d18de653..2e10d9e74 100644 --- a/src/test/java/konkuk/thip/room/adapter/out/jpa/RoomJpaEntityTest.java +++ b/src/test/java/konkuk/thip/room/adapter/out/jpa/RoomJpaEntityTest.java @@ -44,8 +44,8 @@ class RoomJpaEntityTest { void saveAndFindRoom() { // given BookJpaEntity book = bookRepository.save(TestEntityFactory.createBook()); - AliasJpaEntity alias = aliasRepository.save(TestEntityFactory.createAlias()); - CategoryJpaEntity category = categoryRepository.save(TestEntityFactory.createCategory(alias)); + AliasJpaEntity alias = aliasRepository.save(TestEntityFactory.createLiteratureAlias()); + CategoryJpaEntity category = categoryRepository.save(TestEntityFactory.createLiteratureCategory(alias)); RoomJpaEntity room = roomRepository.save(TestEntityFactory.createRoom(book, category)); // when diff --git a/src/test/java/konkuk/thip/room/adapter/out/jpa/VoteJpaEntityTest.java b/src/test/java/konkuk/thip/room/adapter/out/jpa/VoteJpaEntityTest.java index 79f3aea6a..e5cf1d357 100644 --- a/src/test/java/konkuk/thip/room/adapter/out/jpa/VoteJpaEntityTest.java +++ b/src/test/java/konkuk/thip/room/adapter/out/jpa/VoteJpaEntityTest.java @@ -52,10 +52,10 @@ class VoteJpaEntityTest { @DisplayName("VoteJpaEntity 저장 및 조회 테스트") void saveAndFindVote() { // given - AliasJpaEntity alias = aliasRepository.save(TestEntityFactory.createAlias()); + AliasJpaEntity alias = aliasRepository.save(TestEntityFactory.createLiteratureAlias()); UserJpaEntity user = userRepository.save(TestEntityFactory.createUser(alias)); BookJpaEntity book = bookRepository.save(TestEntityFactory.createBook()); - CategoryJpaEntity category = categoryRepository.save(TestEntityFactory.createCategory(alias)); + CategoryJpaEntity category = categoryRepository.save(TestEntityFactory.createLiteratureCategory(alias)); RoomJpaEntity room = roomRepository.save(TestEntityFactory.createRoom(book, category)); VoteJpaEntity vote = voteRepository.save(VoteJpaEntity.builder() diff --git a/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java b/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java index 899d914ac..2d788ed70 100644 --- a/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java +++ b/src/test/java/konkuk/thip/user/adapter/in/web/UserSignupControllerTest.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import konkuk.thip.common.security.util.JwtUtil; +import konkuk.thip.common.util.TestEntityFactory; import konkuk.thip.user.adapter.in.web.request.UserSignupRequest; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; @@ -58,15 +59,10 @@ void tearDown() { @DisplayName("[칭호id, 닉네임] 정보를 바탕으로 회원가입을 진행한다.") void signup_success() throws Exception { //given : alias 생성, 회원가입 request 생성 - AliasJpaEntity aliasJpaEntity = AliasJpaEntity.builder() - .value("칭호") - .color("blue") - .imageUrl("http://image.url") - .build(); - aliasJpaRepository.save(aliasJpaEntity); + AliasJpaEntity aliasJpaEntity = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias()); UserSignupRequest request = new UserSignupRequest( - aliasJpaEntity.getAliasId(), + aliasJpaEntity.getValue(), "테스트유저" ); @@ -86,8 +82,9 @@ void signup_success() throws Exception { Long userId = jsonNode.path("data").path("userId").asLong(); UserJpaEntity userJpaEntity = userJpaRepository.findById(userId).orElse(null); + AliasJpaEntity userAliasJpaEntity = aliasJpaRepository.findByValue(request.aliasName()).orElse(null); - assertThat(userJpaEntity.getAliasForUserJpaEntity().getAliasId()).isEqualTo(request.aliasId()); + assertThat(userAliasJpaEntity.getValue()).isEqualTo(request.aliasName()); assertThat(userJpaEntity.getNickname()).isEqualTo(request.nickname()); } @@ -108,7 +105,7 @@ void signup_alias_id_null() throws Exception { .content(objectMapper.writeValueAsString(request))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.code").value(API_INVALID_PARAM.getCode())) - .andExpect(jsonPath("$.message", containsString("aliasId는 필수입니다."))); + .andExpect(jsonPath("$.message", containsString("aliasName은 필수입니다."))); } @Test @@ -116,7 +113,7 @@ void signup_alias_id_null() throws Exception { void signup_nickname_blank() throws Exception { //given: nickname blank UserSignupRequest request = new UserSignupRequest( - 1L, + "문학가", "" ); @@ -136,7 +133,7 @@ void signup_nickname_blank() throws Exception { void signup_nickname_invalid_pattern() throws Exception { //given: nickname with invalid characters UserSignupRequest request = new UserSignupRequest( - 1L, + "문학가", "닉네임!!" ); @@ -156,7 +153,7 @@ void signup_nickname_invalid_pattern() throws Exception { void signup_nickname_too_long() throws Exception { //given: 11글자 nickname UserSignupRequest request = new UserSignupRequest( - 1L, + "문학가", "11글자닉네임입니다아" ); @@ -175,16 +172,11 @@ void signup_nickname_too_long() throws Exception { @DisplayName("임시 토큰을 통해 @Oauth2Id로 oauth2Id를 정확히 추출하여 회원가입에 성공한다.") void signup_whenValidSignupToken_thenExtractOauth2IdCorrectly() throws Exception { //given : alias 데이터 저장 - AliasJpaEntity aliasJpaEntity = AliasJpaEntity.builder() - .value("테스트칭호") - .color("green") - .imageUrl("http://image.url") - .build(); - aliasJpaRepository.save(aliasJpaEntity); + AliasJpaEntity aliasJpaEntity = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias()); //회원가입 request 생성 UserSignupRequest request = new UserSignupRequest( - aliasJpaEntity.getAliasId(), + aliasJpaEntity.getValue(), "테스트유저" ); @@ -218,7 +210,7 @@ void signup_whenValidSignupToken_thenExtractOauth2IdCorrectly() throws Exception void signup_whenNoToken_thenUnauthorized() throws Exception { //given: aliasId null UserSignupRequest request = new UserSignupRequest( - 1L, + "문학가", "테스트유저" ); diff --git a/src/test/java/konkuk/thip/user/adapter/in/web/UserVerifyNicknameControllerTest.java b/src/test/java/konkuk/thip/user/adapter/in/web/UserVerifyNicknameControllerTest.java index 8afcd9bf8..69b2a9c84 100644 --- a/src/test/java/konkuk/thip/user/adapter/in/web/UserVerifyNicknameControllerTest.java +++ b/src/test/java/konkuk/thip/user/adapter/in/web/UserVerifyNicknameControllerTest.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import konkuk.thip.common.util.TestEntityFactory; import konkuk.thip.user.adapter.in.web.request.UserVerifyNicknameRequest; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; @@ -76,12 +77,7 @@ void verify_nickname_true() throws Exception { @DisplayName("[닉네임]값이 이미 DB에 존재하는 경우, false를 반환한다.") void verify_nickname_false() throws Exception { //given: DB에 "테스트유저" 생성 - AliasJpaEntity aliasJpaEntity = AliasJpaEntity.builder() - .value("칭호") - .color("blue") - .imageUrl("http://image.url") - .build(); - aliasJpaRepository.save(aliasJpaEntity); + AliasJpaEntity aliasJpaEntity = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias()); UserJpaEntity userJpaEntity = UserJpaEntity.builder() .nickname("테스트유저") diff --git a/src/test/java/konkuk/thip/user/adapter/in/web/UserViewAliasChoiceControllerTest.java b/src/test/java/konkuk/thip/user/adapter/in/web/UserViewAliasChoiceControllerTest.java index 15c999bf6..3f73a4610 100644 --- a/src/test/java/konkuk/thip/user/adapter/in/web/UserViewAliasChoiceControllerTest.java +++ b/src/test/java/konkuk/thip/user/adapter/in/web/UserViewAliasChoiceControllerTest.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import konkuk.thip.common.util.TestEntityFactory; import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; import konkuk.thip.room.adapter.out.persistence.CategoryJpaRepository; import konkuk.thip.user.adapter.in.web.response.UserViewAliasChoiceResponse; @@ -73,36 +74,16 @@ void show_alias_choice_view() throws Exception { assertThat(choices) .extracting("aliasName", "categoryName", "imageUrl", "color") .containsExactlyInAnyOrder( - tuple("문학가", "문학", "문학가_image", "red"), - tuple("과학자", "과학/IT", "과학자_image", "blue") + tuple("문학가", "문학", "문학_image", "문학_color"), + tuple("과학자", "과학/IT", "과학_image", "과학_color") ); } private void saveAliasesAndCategories() { - AliasJpaEntity alias1 = AliasJpaEntity.builder() - .value("문학가") - .imageUrl("문학가_image") - .color("red") - .build(); - aliasJpaRepository.save(alias1); - - CategoryJpaEntity category1 = CategoryJpaEntity.builder() - .value("문학") - .aliasForCategoryJpaEntity(alias1) - .build(); - categoryJpaRepository.save(category1); - - AliasJpaEntity alias2 = AliasJpaEntity.builder() - .value("과학자") - .imageUrl("과학자_image") - .color("blue") - .build(); - aliasJpaRepository.save(alias2); - - CategoryJpaEntity category2 = CategoryJpaEntity.builder() - .value("과학/IT") - .aliasForCategoryJpaEntity(alias2) - .build(); - categoryJpaRepository.save(category2); + AliasJpaEntity alias1 = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias()); + CategoryJpaEntity category1 = categoryJpaRepository.save(TestEntityFactory.createLiteratureCategory(alias1)); + + AliasJpaEntity alias2 = aliasJpaRepository.save(TestEntityFactory.createScienceAlias()); + CategoryJpaEntity category2 = categoryJpaRepository.save(TestEntityFactory.createScienceCategory(alias2)); } } diff --git a/src/test/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntityTest.java b/src/test/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntityTest.java index ffd06ca78..c316f8de8 100644 --- a/src/test/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntityTest.java +++ b/src/test/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntityTest.java @@ -33,7 +33,7 @@ class UserJpaEntityTest { @DisplayName("UserJpaEntity 저장 및 조회 테스트") void saveAndFindUser() { // given - AliasJpaEntity alias = aliasJpaRepository.save(TestEntityFactory.createAlias()); + AliasJpaEntity alias = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias()); UserJpaEntity user = userRepository.save(TestEntityFactory.createUser(alias)); // when @@ -44,7 +44,7 @@ void saveAndFindUser() { // then assertThat(foundUser.getNickname()).isEqualTo("테스터"); - assertThat(foundUser.getAliasForUserJpaEntity().getValue()).isEqualTo("칭호"); + assertThat(foundUser.getAliasForUserJpaEntity().getValue()).isEqualTo("문학가"); assertThat(foundUser.getRole()).isEqualTo(UserRole.USER); } } \ No newline at end of file From cf958bf66e897e4386b6c0d6b8b322dd85510f50 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 17:44:59 +0900 Subject: [PATCH 24/31] =?UTF-8?q?[test]=20DataJpaTest=EB=8A=94=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=20=EB=A1=A4=EB=B0=B1=EB=90=98=EB=AF=80=EB=A1=9C=20Aft?= =?UTF-8?q?erEach=20=EC=A0=9C=EA=B1=B0=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../out/persistence/RecordQueryRepositoryImplTest.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/test/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImplTest.java b/src/test/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImplTest.java index 62f26ca83..0c79957b0 100644 --- a/src/test/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImplTest.java +++ b/src/test/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImplTest.java @@ -89,16 +89,6 @@ void setUp() { em.clear(); } -// @AfterEach -// void tearDown() { -// em.createQuery("DELETE FROM RecordJpaEntity").executeUpdate(); -// em.createQuery("DELETE FROM RoomJpaEntity").executeUpdate(); -// em.createQuery("DELETE FROM UserJpaEntity").executeUpdate(); -// em.createQuery("DELETE FROM AliasJpaEntity").executeUpdate(); -// em.createQuery("DELETE FROM CategoryJpaEntity").executeUpdate(); -// em.createQuery("DELETE FROM BookJpaEntity").executeUpdate(); -// } - @Test @DisplayName("기본 조회 및 페이징 동작 확인") void test_paging() { From 96fee95aeea3ac8cfef6fee4ca415f53c25772a2 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 18:11:12 +0900 Subject: [PATCH 25/31] =?UTF-8?q?[refactor]=20count=20=EC=BF=BC=EB=A6=AC?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/RecordQueryRepositoryImpl.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java index 03ebf6c60..3f1814cc0 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java +++ b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java @@ -3,15 +3,12 @@ import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.Order; import com.querydsl.core.types.OrderSpecifier; -import com.querydsl.core.types.dsl.NumberTemplate; import com.querydsl.jpa.impl.JPAQueryFactory; -import konkuk.thip.comment.adapter.out.jpa.QCommentJpaEntity; import konkuk.thip.common.exception.InvalidStateException; import konkuk.thip.common.exception.code.ErrorCode; import konkuk.thip.common.util.DateUtil; import konkuk.thip.post.adapter.out.jpa.PostJpaEntity; import konkuk.thip.post.adapter.out.jpa.QPostJpaEntity; -import konkuk.thip.post.adapter.out.jpa.QPostLikeJpaEntity; import konkuk.thip.record.adapter.in.web.response.RecordDto; import konkuk.thip.record.adapter.in.web.response.RecordSearchResponse; import konkuk.thip.record.adapter.in.web.response.VoteDto; @@ -49,6 +46,8 @@ public Page findRecordsByRoom(Long room where.and(post.instanceOf(RecordJpaEntity.class).and(record.isOverview.isTrue())) .or(post.instanceOf(VoteJpaEntity.class).and(vote.isOverview.isTrue())); } else { + where.and(post.instanceOf(RecordJpaEntity.class).and(record.isOverview.isFalse())) + .or(post.instanceOf(VoteJpaEntity.class).and(vote.isOverview.isFalse())); where.and(post.instanceOf(RecordJpaEntity.class).and(record.page.between(pageStart, pageEnd))) .or(post.instanceOf(VoteJpaEntity.class).and(vote.page.between(pageStart, pageEnd))); } @@ -63,13 +62,11 @@ public Page findRecordsByRoom(Long room boolean asc = order.getDirection().isAscending(); if ("likeCount".equalsIgnoreCase(property)) { - NumberTemplate likeCountTemplate = com.querydsl.core.types.dsl.Expressions.numberTemplate( - Long.class, "count({0})", QPostLikeJpaEntity.postLikeJpaEntity.likeId); - orderSpecifiers.add(new OrderSpecifier<>(asc ? Order.ASC : Order.DESC, likeCountTemplate)); + orderSpecifiers.add(new OrderSpecifier<>(asc ? Order.ASC : Order.DESC, + record.likeCount.coalesce(0).add(vote.likeCount.coalesce(0)))); } else if ("commentCount".equalsIgnoreCase(property)) { - NumberTemplate commentCountTemplate = com.querydsl.core.types.dsl.Expressions.numberTemplate( - Long.class, "count({0})", QCommentJpaEntity.commentJpaEntity.commentId); - orderSpecifiers.add(new OrderSpecifier<>(asc ? Order.ASC : Order.DESC, commentCountTemplate)); + orderSpecifiers.add(new OrderSpecifier<>(asc ? Order.ASC : Order.DESC, + record.commentCount.coalesce(0).add(vote.commentCount.coalesce(0)))); } else if ("createdAt".equalsIgnoreCase(property)) { orderSpecifiers.add(asc ? post.createdAt.asc() : post.createdAt.desc()); } else { From 2b825e72d07a175ee7386e3db9b073395eb557f0 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 18:11:22 +0900 Subject: [PATCH 26/31] =?UTF-8?q?[test]=20=EC=A0=95=EB=A0=AC=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RecordQueryRepositoryImplTest.java | 58 +++++++++++++++---- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/src/test/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImplTest.java b/src/test/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImplTest.java index 0c79957b0..3ece406e6 100644 --- a/src/test/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImplTest.java +++ b/src/test/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImplTest.java @@ -64,8 +64,8 @@ void setUp() { .userJpaEntity(i % 2 == 0 ? user1 : user2) .roomJpaEntity(room) .content("레코드 " + i) - .likeCount(1) - .commentCount(1) + .likeCount(10 - i) + .commentCount(i) .isOverview(false) .page(1) .build(); @@ -126,6 +126,24 @@ void test_viewType_mine() { assertThat(result.getNumberOfElements()).isEqualTo(5); // user1이 작성한 레코드가 5개 } + @Test + @DisplayName("isOverview가 true일 때 레코드가 총평 기록만 조회된다.") + void test_isOverview_true() { + Page result = recordQueryRepository.findRecordsByRoom( + room.getRoomId(), + "group", + 1, + 1, + true, + user1.getUserId(), + PageRequest.of(0, 10) + ); + + assertThat(result.getNumberOfElements()).isEqualTo(10); + assertThat(result.getContent()).allSatisfy(record -> + assertThat(record.page()).isEqualTo(room.getBookJpaEntity().getPageCount())); + } + @Test @DisplayName("latest 기준 정렬 확인") void test_sortingBy_latest() { @@ -146,20 +164,40 @@ void test_sortingBy_latest() { } @Test - @DisplayName("isOverview가 true일 때 레코드가 총평 기록만 조회된다.") - void test_isOverview_true() { + @DisplayName("likeCount 기준 정렬 확인") + void test_sortingBy_likeCount() { Page result = recordQueryRepository.findRecordsByRoom( room.getRoomId(), - "group", + null, 1, 1, - true, + false, user1.getUserId(), - PageRequest.of(0, 10) + PageRequest.of(0, 10, org.springframework.data.domain.Sort.by("likeCount").descending()) ); - assertThat(result.getNumberOfElements()).isEqualTo(10); - assertThat(result.getContent()).allSatisfy(record -> - assertThat(record.page()).isEqualTo(room.getBookJpaEntity().getPageCount())); + List content = result.getContent(); + for (int i = 1; i < content.size(); i++) { + assertThat(content.get(i - 1).likeCount()).isGreaterThanOrEqualTo(content.get(i).likeCount()); + } + } + + @Test + @DisplayName("commentCount 기준 정렬 확인") + void test_sortingBy_commentCount() { + Page result = recordQueryRepository.findRecordsByRoom( + room.getRoomId(), + null, + 1, + 1, + false, + user1.getUserId(), + PageRequest.of(0, 10, org.springframework.data.domain.Sort.by("commentCount").descending()) + ); + + List content = result.getContent(); + for (int i = 1; i < content.size(); i++) { + assertThat(content.get(i - 1).commentCount()).isGreaterThanOrEqualTo(content.get(i).commentCount()); + } } } \ No newline at end of file From a9edaf442e26aa43f5fbcbf89e722e7147634d89 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 18:11:37 +0900 Subject: [PATCH 27/31] =?UTF-8?q?[refactor]=20=EC=9E=98=EB=AA=BB=EB=90=9C?= =?UTF-8?q?=20=EC=98=88=EC=99=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20import=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/out/persistence/RecordSearchSortParams.java | 3 +-- .../adapter/out/persistence/RecordSearchTypeParams.java | 3 +-- .../record/application/service/RecordSearchService.java | 6 +++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordSearchSortParams.java b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordSearchSortParams.java index 46103e9ed..ebaa373b7 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordSearchSortParams.java +++ b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordSearchSortParams.java @@ -1,6 +1,5 @@ package konkuk.thip.record.adapter.out.persistence; -import com.sun.jdi.request.InvalidRequestStateException; import konkuk.thip.common.exception.InvalidStateException; import konkuk.thip.common.exception.code.ErrorCode; import lombok.Getter; @@ -25,7 +24,7 @@ public static RecordSearchSortParams from(String value) { .filter(param -> param.getValue().equals(value)) .findFirst() .orElseThrow( - () -> new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("현재 정렬 조건 param : " + value)) + () -> new InvalidStateException(ErrorCode.API_INVALID_PARAM, new IllegalArgumentException("현재 정렬 조건 param : " + value)) ); } } diff --git a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordSearchTypeParams.java b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordSearchTypeParams.java index 278121d70..615a13ad7 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordSearchTypeParams.java +++ b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordSearchTypeParams.java @@ -1,6 +1,5 @@ package konkuk.thip.record.adapter.out.persistence; -import com.sun.jdi.request.InvalidRequestStateException; import konkuk.thip.common.exception.InvalidStateException; import konkuk.thip.common.exception.code.ErrorCode; import lombok.Getter; @@ -23,7 +22,7 @@ public static RecordSearchTypeParams from(String value) { .filter(param -> param.getValue().equals(value)) .findFirst() .orElseThrow( - () -> new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("현재 타입 조건 param : " + value)) + () -> new InvalidStateException(ErrorCode.API_INVALID_PARAM, new IllegalArgumentException("현재 타입 조건 param : " + value)) ); } } diff --git a/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java b/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java index 30ad7aeb0..9fb716ce7 100644 --- a/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java +++ b/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java @@ -102,13 +102,13 @@ public RecordSearchResponse search(Long roomId, String type, String sort, Intege private void validatePageStartAndEnd(Integer pageStart, Integer pageEnd, Boolean isOverview) { if((pageStart != null && pageEnd == null) || (pageStart == null && pageEnd != null)) { - throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("pageStart와 pageEnd는 모두 설정되거나(특정 페이지 조회) 모두 설정되지 않아야 합니다.(전체 페이지 조회)")); + throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new IllegalArgumentException("pageStart와 pageEnd는 모두 설정되거나(특정 페이지 조회) 모두 설정되지 않아야 합니다.(전체 페이지 조회)")); } if (pageStart != null && pageEnd != null && pageStart > pageEnd) { - throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("pageStart는 pageEnd보다 작거나 같아야 합니다.")); + throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new IllegalArgumentException("pageStart는 pageEnd보다 작거나 같아야 합니다.")); } if (isOverview && (pageStart != null || pageEnd != null)) { - throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("pageStart와 pageEnd는 isOverview가 true일 때 유효한 파라미터가 아닙니다.")); + throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new IllegalArgumentException("pageStart와 pageEnd는 isOverview가 true일 때 유효한 파라미터가 아닙니다.")); } } From 4dcff142c251baafdd73fc8d6d49261fd5a1b598 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 12 Jul 2025 18:16:42 +0900 Subject: [PATCH 28/31] =?UTF-8?q?[refactor]=20=EC=9E=98=EB=AA=BB=EB=90=9C?= =?UTF-8?q?=20=EC=98=88=EC=99=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20import=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/record/application/service/RecordSearchService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java b/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java index 9fb716ce7..dacac95f6 100644 --- a/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java +++ b/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java @@ -1,6 +1,5 @@ package konkuk.thip.record.application.service; -import com.sun.jdi.request.InvalidRequestStateException; import konkuk.thip.book.application.port.out.BookCommandPort; import konkuk.thip.book.domain.Book; import konkuk.thip.common.exception.InvalidStateException; @@ -117,7 +116,7 @@ private Integer validatePageNum(Integer pageNum) { return 1; // 기본값으로 첫 페이지 반환 } if (pageNum < 1) { - throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new InvalidRequestStateException("pageNum은 1 이상의 값이어야 합니다.")); + throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new IllegalArgumentException("pageNum은 1 이상의 값이어야 합니다.")); } return pageNum; } From eb311430702b236eb0f66bc04073b2eed760d4e7 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sun, 13 Jul 2025 02:15:40 +0900 Subject: [PATCH 29/31] =?UTF-8?q?[refactor]=20count=20=EB=B3=80=EC=88=98?= =?UTF-8?q?=20NPE=20=EB=B0=A9=EC=A7=80=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../out/persistence/RecordQueryRepositoryImpl.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java index 3f1814cc0..782abad9a 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java +++ b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; @Repository @RequiredArgsConstructor @@ -94,8 +95,8 @@ public Page findRecordsByRoom(Long room .nickName(r.getUserJpaEntity().getNickname()) .profileImageUrl(r.getUserJpaEntity().getImageUrl()) .content(r.getContent()) - .likeCount(r.getLikeCount()) - .commentCount(r.getCommentCount()) + .likeCount(Optional.ofNullable(r.getLikeCount()).map(Number::intValue).orElse(0)) + .commentCount(Optional.ofNullable(r.getCommentCount()).map(Number::intValue).orElse(0)) .isLiked(false) // 초기값은 false, 서비스 레벨에서 처리 .isWriter(loginUserId.equals(r.getUserJpaEntity().getUserId())) .recordId(r.getPostId()) @@ -109,8 +110,8 @@ public Page findRecordsByRoom(Long room .nickName(v.getUserJpaEntity().getNickname()) .profileImageUrl(v.getUserJpaEntity().getImageUrl()) .content(v.getContent()) - .likeCount(v.getLikeCount()) - .commentCount(v.getCommentCount()) + .likeCount(Optional.ofNullable(v.getLikeCount()).map(Number::intValue).orElse(0)) + .commentCount(Optional.ofNullable(v.getCommentCount()).map(Number::intValue).orElse(0)) .isLiked(false) // 초기값은 false, 서비스 레벨에서 처리 .isWriter(loginUserId.equals(v.getUserJpaEntity().getUserId())) .voteId(v.getPostId()) From f6b8e434ee02a500fe29b3e227cf9f5f9b569359 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sun, 13 Jul 2025 02:41:43 +0900 Subject: [PATCH 30/31] =?UTF-8?q?[refactor]=20=ED=95=84=ED=84=B0=EB=A7=81?= =?UTF-8?q?=20=EB=85=BC=EB=A6=AC=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20=EB=B0=8F=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EC=B6=9C?= =?UTF-8?q?=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RecordQueryRepositoryImpl.java | 93 ++++++++++++------- 1 file changed, 60 insertions(+), 33 deletions(-) diff --git a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java index 782abad9a..f5d0edf24 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java +++ b/src/main/java/konkuk/thip/record/adapter/out/persistence/RecordQueryRepositoryImpl.java @@ -40,40 +40,14 @@ public Page findRecordsByRoom(Long room QVoteJpaEntity vote = QVoteJpaEntity.voteJpaEntity; BooleanBuilder where = new BooleanBuilder(); - where.and(post.instanceOf(RecordJpaEntity.class).and(record.roomJpaEntity.roomId.eq(roomId))) - .or(post.instanceOf(VoteJpaEntity.class).and(vote.roomJpaEntity.roomId.eq(roomId))); - - if (isOverview) { - where.and(post.instanceOf(RecordJpaEntity.class).and(record.isOverview.isTrue())) - .or(post.instanceOf(VoteJpaEntity.class).and(vote.isOverview.isTrue())); - } else { - where.and(post.instanceOf(RecordJpaEntity.class).and(record.isOverview.isFalse())) - .or(post.instanceOf(VoteJpaEntity.class).and(vote.isOverview.isFalse())); - where.and(post.instanceOf(RecordJpaEntity.class).and(record.page.between(pageStart, pageEnd))) - .or(post.instanceOf(VoteJpaEntity.class).and(vote.page.between(pageStart, pageEnd))); - } + where.and(buildRecordCondition(roomId, pageStart, pageEnd, isOverview, post, record). + or(buildVoteCondition(roomId, pageStart, pageEnd, isOverview, post, vote))); if ("mine".equals(viewType)) { where.and(post.userJpaEntity.userId.eq(loginUserId)); } - List> orderSpecifiers = new ArrayList<>(); - for (Sort.Order order : pageable.getSort()) { - String property = order.getProperty(); - boolean asc = order.getDirection().isAscending(); - - if ("likeCount".equalsIgnoreCase(property)) { - orderSpecifiers.add(new OrderSpecifier<>(asc ? Order.ASC : Order.DESC, - record.likeCount.coalesce(0).add(vote.likeCount.coalesce(0)))); - } else if ("commentCount".equalsIgnoreCase(property)) { - orderSpecifiers.add(new OrderSpecifier<>(asc ? Order.ASC : Order.DESC, - record.commentCount.coalesce(0).add(vote.commentCount.coalesce(0)))); - } else if ("createdAt".equalsIgnoreCase(property)) { - orderSpecifiers.add(asc ? post.createdAt.asc() : post.createdAt.desc()); - } else { - orderSpecifiers.add(post.createdAt.desc()); - } - } + List> orderSpecifiers = createOrderSpecifiers(pageable, record, vote, post); List posts = queryFactory .selectFrom(post) @@ -95,8 +69,8 @@ public Page findRecordsByRoom(Long room .nickName(r.getUserJpaEntity().getNickname()) .profileImageUrl(r.getUserJpaEntity().getImageUrl()) .content(r.getContent()) - .likeCount(Optional.ofNullable(r.getLikeCount()).map(Number::intValue).orElse(0)) - .commentCount(Optional.ofNullable(r.getCommentCount()).map(Number::intValue).orElse(0)) + .likeCount(safeInt(r.getLikeCount())) + .commentCount(safeInt(r.getCommentCount())) .isLiked(false) // 초기값은 false, 서비스 레벨에서 처리 .isWriter(loginUserId.equals(r.getUserJpaEntity().getUserId())) .recordId(r.getPostId()) @@ -110,8 +84,8 @@ public Page findRecordsByRoom(Long room .nickName(v.getUserJpaEntity().getNickname()) .profileImageUrl(v.getUserJpaEntity().getImageUrl()) .content(v.getContent()) - .likeCount(Optional.ofNullable(v.getLikeCount()).map(Number::intValue).orElse(0)) - .commentCount(Optional.ofNullable(v.getCommentCount()).map(Number::intValue).orElse(0)) + .likeCount(safeInt(v.getLikeCount())) + .commentCount(safeInt(v.getCommentCount())) .isLiked(false) // 초기값은 false, 서비스 레벨에서 처리 .isWriter(loginUserId.equals(v.getUserJpaEntity().getUserId())) .voteId(v.getPostId()) @@ -135,4 +109,57 @@ public Page findRecordsByRoom(Long room return new PageImpl<>(resultList, pageable, total); } + + private Integer safeInt(Number number) { + return Optional.ofNullable(number).map(Number::intValue).orElse(0); + } + + private List> createOrderSpecifiers(Pageable pageable, QRecordJpaEntity record, QVoteJpaEntity vote, QPostJpaEntity post) { + List> orderSpecifiers = new ArrayList<>(); + for (Sort.Order order : pageable.getSort()) { + String property = order.getProperty(); + boolean asc = order.getDirection().isAscending(); + + if ("likeCount".equalsIgnoreCase(property)) { + orderSpecifiers.add(new OrderSpecifier<>(asc ? Order.ASC : Order.DESC, + record.likeCount.coalesce(0).add(vote.likeCount.coalesce(0)))); + } else if ("commentCount".equalsIgnoreCase(property)) { + orderSpecifiers.add(new OrderSpecifier<>(asc ? Order.ASC : Order.DESC, + record.commentCount.coalesce(0).add(vote.commentCount.coalesce(0)))); + } else if ("createdAt".equalsIgnoreCase(property)) { + orderSpecifiers.add(asc ? post.createdAt.asc() : post.createdAt.desc()); + } else { + orderSpecifiers.add(post.createdAt.desc()); + } + } + return orderSpecifiers; + } + + private BooleanBuilder buildVoteCondition(Long roomId, Integer pageStart, Integer pageEnd, Boolean isOverview, QPostJpaEntity post, QVoteJpaEntity vote) { + BooleanBuilder voteCondition = new BooleanBuilder(); + voteCondition.and(post.instanceOf(VoteJpaEntity.class)) + .and(vote.roomJpaEntity.roomId.eq(roomId)); + + if (isOverview) { + voteCondition.and(vote.isOverview.isTrue()); + } else { + voteCondition.and(vote.isOverview.isFalse()) + .and(vote.page.between(pageStart, pageEnd)); + } + return voteCondition; + } + + private BooleanBuilder buildRecordCondition(Long roomId, Integer pageStart, Integer pageEnd, Boolean isOverview, QPostJpaEntity post, QRecordJpaEntity record) { + BooleanBuilder recordCondition = new BooleanBuilder(); + recordCondition.and(post.instanceOf(RecordJpaEntity.class)) + .and(record.roomJpaEntity.roomId.eq(roomId)); + + if (isOverview) { + recordCondition.and(record.isOverview.isTrue()); + } else { + recordCondition.and(record.isOverview.isFalse()) + .and(record.page.between(pageStart, pageEnd)); + } + return recordCondition; + } } \ No newline at end of file From 88f021c2a89233835582939046954132691bc5b1 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sun, 13 Jul 2025 17:53:21 +0900 Subject: [PATCH 31/31] =?UTF-8?q?[refactor]=20recordCount=200=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=B4=88=EA=B8=B0=ED=99=94=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/konkuk/thip/feed/domain/Feed.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/konkuk/thip/feed/domain/Feed.java b/src/main/java/konkuk/thip/feed/domain/Feed.java index c4c01aa2f..4e7af7b9b 100644 --- a/src/main/java/konkuk/thip/feed/domain/Feed.java +++ b/src/main/java/konkuk/thip/feed/domain/Feed.java @@ -32,13 +32,13 @@ public class Feed extends BaseDomainEntity { private List tagList; - public static Feed withoutId(String content, Long creatorId, Boolean isPublic, int reportCount, Long targetBookId, List tagList) { + public static Feed withoutId(String content, Long creatorId, Boolean isPublic, Long targetBookId, List tagList) { return Feed.builder() .id(null) .content(content) .creatorId(creatorId) .isPublic(isPublic) - .reportCount(reportCount) + .reportCount(0) .likeCount(0) .commentCount(0) .targetBookId(targetBookId)