From c703662889f28025197a1b54950f92ad9947af15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=8A=B9=EB=AF=BC?= <101695929+min429@users.noreply.github.com> Date: Fri, 30 Aug 2024 15:20:36 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=20api=EB=A5=BC?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=ED=95=9C=EB=8B=A4.=20(#40)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 검색 api를 구현한다. - 문자열 비교를 통한 검색 기능 구현 * refactor: 중복 코드를 메소드로 추출한다. * feat: 커서 기반 페이징을 위해 dto를 수정 및 추가한다. * feat: 커서 기반 페이징을 위해 관련 controller, service를 수정한다. * feat: 커서 기반 페이징 방식으로 쿼리를 재구성한다. * refactor: 메소드 위치를 변경하고 중복 메소드를 추출한다. * feat: Controller 메소드 매핑 방식을 변경하고 dto에 nullable을 추가한다.(swagger에 명시) * feat: 검색 쿼리 대상에서 content를 제외한다. --- .../api/AccompanyBoardController.java | 25 ++-- .../api/AccompanyRequestController.java | 23 ++-- .../api/dto/FindApplicantDetailsResult.java | 33 +++-- .../api/dto/FindBoardThumbnailsResult.java | 40 ++++-- .../accompany/api/dto/FindSlicesResult.java | 34 +++++ .../domain/accompany/api/dto/PageRequest.java | 42 ++++++ .../accompany/api/dto/PageResponse.java | 3 +- .../domain/accompany/entity/enums/Region.java | 13 ++ .../AccompanyBoardRepositoryImpl.java | 128 +++++++++++++----- .../AccompanyRequestRepositoryImpl.java | 50 +++---- .../AccompanyBoardRepositoryCustom.java | 6 +- .../AccompanyRequestRepositoryCustom.java | 4 +- .../service/AccompanyBoardService.java | 37 +++-- .../service/AccompanyRequestService.java | 42 +++--- 14 files changed, 343 insertions(+), 137 deletions(-) create mode 100644 src/main/java/com/dnd/accompany/domain/accompany/api/dto/FindSlicesResult.java create mode 100644 src/main/java/com/dnd/accompany/domain/accompany/api/dto/PageRequest.java diff --git a/src/main/java/com/dnd/accompany/domain/accompany/api/AccompanyBoardController.java b/src/main/java/com/dnd/accompany/domain/accompany/api/AccompanyBoardController.java index c97fe67..a265c1a 100644 --- a/src/main/java/com/dnd/accompany/domain/accompany/api/AccompanyBoardController.java +++ b/src/main/java/com/dnd/accompany/domain/accompany/api/AccompanyBoardController.java @@ -18,6 +18,7 @@ import com.dnd.accompany.domain.accompany.api.dto.CreateAccompanyBoardRequest; import com.dnd.accompany.domain.accompany.api.dto.CreateAccompanyBoardResponse; import com.dnd.accompany.domain.accompany.api.dto.CreateAccompanyRequest; +import com.dnd.accompany.domain.accompany.api.dto.PageRequest; import com.dnd.accompany.domain.accompany.api.dto.PageResponse; import com.dnd.accompany.domain.accompany.api.dto.ReadAccompanyBoardResponse; import com.dnd.accompany.domain.accompany.entity.enums.Region; @@ -49,14 +50,20 @@ public ResponseEntity create( return ResponseEntity.ok(accompanyServiceFacade.createBoard(user.getId(), request)); } + @Operation(summary = "동행글 검색") + @PostMapping("/search") + public ResponseEntity> search( + @RequestBody @Valid PageRequest request, + @RequestParam(value = "keyword") String keyword) { + return ResponseEntity.ok(accompanyBoardService.getMatchedBoards(request, keyword)); + } + @Operation(summary = "동행글 목록 조회") - @GetMapping + @PostMapping("/all") public ResponseEntity> readAll( - @PageableDefault( - sort = {"updatedAt", "createdAt"}, - direction = Sort.Direction.DESC) Pageable pageable, + @RequestBody @Valid PageRequest request, @RequestParam(value = "region", required = false) Region region) { - return ResponseEntity.ok(accompanyBoardService.getAllBoards(pageable, region)); + return ResponseEntity.ok(accompanyBoardService.getAllBoards(request, region)); } @Operation(summary = "동행글 상세 조회") @@ -93,12 +100,10 @@ public ResponseEntity remove( } @Operation(summary = "동행 기록 조회") - @GetMapping("/records") + @PostMapping("/records") public ResponseEntity> readAllRecords( - @PageableDefault( - sort = {"createdAt"}, - direction = Sort.Direction.DESC) Pageable pageable, + @RequestBody @Valid PageRequest request, @AuthenticationPrincipal JwtAuthentication user) { - return ResponseEntity.ok(accompanyBoardService.getAllRecords(pageable, user.getId())); + return ResponseEntity.ok(accompanyBoardService.getAllRecords(request, user.getId())); } } diff --git a/src/main/java/com/dnd/accompany/domain/accompany/api/AccompanyRequestController.java b/src/main/java/com/dnd/accompany/domain/accompany/api/AccompanyRequestController.java index 86f8310..8ecfe79 100644 --- a/src/main/java/com/dnd/accompany/domain/accompany/api/AccompanyRequestController.java +++ b/src/main/java/com/dnd/accompany/domain/accompany/api/AccompanyRequestController.java @@ -7,10 +7,12 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import com.dnd.accompany.domain.accompany.api.dto.PageRequest; import com.dnd.accompany.domain.accompany.api.dto.PageResponse; import com.dnd.accompany.domain.accompany.api.dto.ReadAccompanyResponse; import com.dnd.accompany.domain.accompany.api.dto.ReceivedAccompany; @@ -20,9 +22,8 @@ import com.dnd.accompany.domain.auth.dto.jwt.JwtAuthentication; import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; @Tag(name = "AccompanyRequest") @@ -35,23 +36,19 @@ public class AccompanyRequestController { private final AccompanyRequestService accompanyRequestService; @Operation(summary = "보낸 동행 신청서 목록 조회") - @GetMapping("/sended") + @PostMapping("/sended") public ResponseEntity> readAllSended( - @PageableDefault( - sort = {"updatedAt", "createdAt"}, - direction = Sort.Direction.DESC) Pageable pageable, + @RequestBody @Valid PageRequest request, @AuthenticationPrincipal JwtAuthentication user) { - return ResponseEntity.ok(accompanyRequestService.getAllSendedAccompanies(pageable, user.getId())); + return ResponseEntity.ok(accompanyRequestService.getAllSendedAccompanies(request, user.getId())); } @Operation(summary = "받은 동행 신청서 목록 조회") - @GetMapping("/received") + @PostMapping("/received") public ResponseEntity> readAllReceived( - @PageableDefault( - sort = {"updatedAt", "createdAt"}, - direction = Sort.Direction.DESC) Pageable pageable, + @RequestBody @Valid PageRequest request, @AuthenticationPrincipal JwtAuthentication user) { - return ResponseEntity.ok(accompanyRequestService.getAllReceivedAccompanies(pageable, user.getId())); + return ResponseEntity.ok(accompanyRequestService.getAllReceivedAccompanies(request, user.getId())); } @Operation(summary = "동행 신청서 조회") diff --git a/src/main/java/com/dnd/accompany/domain/accompany/api/dto/FindApplicantDetailsResult.java b/src/main/java/com/dnd/accompany/domain/accompany/api/dto/FindApplicantDetailsResult.java index ef9d713..1fa7309 100644 --- a/src/main/java/com/dnd/accompany/domain/accompany/api/dto/FindApplicantDetailsResult.java +++ b/src/main/java/com/dnd/accompany/domain/accompany/api/dto/FindApplicantDetailsResult.java @@ -5,14 +5,31 @@ import java.util.Arrays; import java.util.List; -public record FindApplicantDetailsResult( - Long requestId, - Long userId, - String nickname, - String provider, - String profileImageUrl, - String imageUrls -) { +import lombok.Getter; +import lombok.experimental.SuperBuilder; + +@Getter +@SuperBuilder +public class FindApplicantDetailsResult extends FindSlicesResult { + private final Long requestId; + private final Long userId; + private final String nickname; + private final String provider; + private final String profileImageUrl; + private final String imageUrls; + + public FindApplicantDetailsResult(Long requestId, Long userId, String nickname, String provider, + String profileImageUrl, String imageUrls, String cursor) { + super(cursor); + + this.requestId = requestId; + this.userId = userId; + this.nickname = nickname; + this.provider = provider; + this.profileImageUrl = profileImageUrl; + this.imageUrls = imageUrls; + } + public List getImageUrlsAsList() { if (imageUrls == null || imageUrls.isEmpty()) { return emptyList(); diff --git a/src/main/java/com/dnd/accompany/domain/accompany/api/dto/FindBoardThumbnailsResult.java b/src/main/java/com/dnd/accompany/domain/accompany/api/dto/FindBoardThumbnailsResult.java index ae02b09..36e6ddd 100644 --- a/src/main/java/com/dnd/accompany/domain/accompany/api/dto/FindBoardThumbnailsResult.java +++ b/src/main/java/com/dnd/accompany/domain/accompany/api/dto/FindBoardThumbnailsResult.java @@ -8,15 +8,37 @@ import com.dnd.accompany.domain.accompany.entity.enums.Region; -public record FindBoardThumbnailsResult( - Long requestId, - String title, - Region region, - LocalDateTime startDate, - LocalDateTime endDate, - String nickname, - String imageUrls -) { +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@Getter +@SuperBuilder +public class FindBoardThumbnailsResult extends FindSlicesResult{ + private final Long requestId; + private final String title; + private final Region region; + private final LocalDateTime startDate; + private final LocalDateTime endDate; + private final String nickname; + private final String imageUrls; + + public FindBoardThumbnailsResult(Long requestId, String title, Region region, + LocalDateTime startDate, LocalDateTime endDate, + String nickname, String imageUrls, String cursor) { + super(cursor); + + this.requestId = requestId; + this.title = title; + this.region = region; + this.startDate = startDate; + this.endDate = endDate; + this.nickname = nickname; + this.imageUrls = imageUrls; + } + public List getImageUrlsAsList() { if (imageUrls == null || imageUrls.isEmpty()) { return emptyList(); diff --git a/src/main/java/com/dnd/accompany/domain/accompany/api/dto/FindSlicesResult.java b/src/main/java/com/dnd/accompany/domain/accompany/api/dto/FindSlicesResult.java new file mode 100644 index 0000000..5aa7258 --- /dev/null +++ b/src/main/java/com/dnd/accompany/domain/accompany/api/dto/FindSlicesResult.java @@ -0,0 +1,34 @@ +package com.dnd.accompany.domain.accompany.api.dto; + +import java.util.List; + +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Slice; +import org.springframework.data.domain.SliceImpl; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@Getter +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@SuperBuilder +public abstract class FindSlicesResult { + private final String cursor; + + public static String getLastCursor(List result) { + return result.get(result.size() - 1).getCursor(); + } + + public static Slice createSlice(int size, List content) { + boolean hasNext = content.size() > size; + + if (hasNext) { + content.remove(content.size() - 1); + } + + return new SliceImpl<>(content, PageRequest.of(0, size), hasNext); + } +} diff --git a/src/main/java/com/dnd/accompany/domain/accompany/api/dto/PageRequest.java b/src/main/java/com/dnd/accompany/domain/accompany/api/dto/PageRequest.java new file mode 100644 index 0000000..f181feb --- /dev/null +++ b/src/main/java/com/dnd/accompany/domain/accompany/api/dto/PageRequest.java @@ -0,0 +1,42 @@ +package com.dnd.accompany.domain.accompany.api.dto; + +import java.time.LocalDateTime; + +import org.springframework.boot.context.properties.bind.DefaultValue; +import org.springframework.lang.Nullable; + +import com.querydsl.core.BooleanBuilder; +import com.querydsl.core.types.dsl.DateTimePath; +import com.querydsl.core.types.dsl.Expressions; +import com.querydsl.core.types.dsl.NumberPath; +import com.querydsl.core.types.dsl.NumberTemplate; +import com.querydsl.core.types.dsl.StringTemplate; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record PageRequest( + @Schema(nullable = true) String cursor, + @Schema(nullable = true) Integer size +) { + + public PageRequest { + if (size == null) { + size = 10; + } + } + + public static BooleanBuilder cursorCondition(String cursor, DateTimePath updatedAt, NumberPath id) { + BooleanBuilder builder = new BooleanBuilder(); + + if (cursor == null) return builder; + + StringTemplate nextCursor = Expressions.stringTemplate( + "CONCAT(DATE_FORMAT({0}, '%Y%m%d%H%i%S'), LPAD(CAST({1} AS STRING), 6, '0'))", + updatedAt, + id + ); + builder.and(nextCursor.lt(cursor)); + + return builder; + } +} diff --git a/src/main/java/com/dnd/accompany/domain/accompany/api/dto/PageResponse.java b/src/main/java/com/dnd/accompany/domain/accompany/api/dto/PageResponse.java index 556304e..70c2d8f 100644 --- a/src/main/java/com/dnd/accompany/domain/accompany/api/dto/PageResponse.java +++ b/src/main/java/com/dnd/accompany/domain/accompany/api/dto/PageResponse.java @@ -4,6 +4,7 @@ public record PageResponse( boolean hasNext, - List data + List data, + String cursor ) { } \ No newline at end of file diff --git a/src/main/java/com/dnd/accompany/domain/accompany/entity/enums/Region.java b/src/main/java/com/dnd/accompany/domain/accompany/entity/enums/Region.java index 0125de8..2defc28 100644 --- a/src/main/java/com/dnd/accompany/domain/accompany/entity/enums/Region.java +++ b/src/main/java/com/dnd/accompany/domain/accompany/entity/enums/Region.java @@ -15,4 +15,17 @@ public enum Region { Region(String description) { this.description = description; } + + public String description() { + return description; + } + + public static Region from(String keyword) { + for (Region region : Region.values()) { + if (region.description().contains(keyword)) { + return region; + } + } + return null; + } } diff --git a/src/main/java/com/dnd/accompany/domain/accompany/infrastructure/querydsl/AccompanyBoardRepositoryImpl.java b/src/main/java/com/dnd/accompany/domain/accompany/infrastructure/querydsl/AccompanyBoardRepositoryImpl.java index 10cf31f..3bbb0f3 100644 --- a/src/main/java/com/dnd/accompany/domain/accompany/infrastructure/querydsl/AccompanyBoardRepositoryImpl.java +++ b/src/main/java/com/dnd/accompany/domain/accompany/infrastructure/querydsl/AccompanyBoardRepositoryImpl.java @@ -1,8 +1,12 @@ package com.dnd.accompany.domain.accompany.infrastructure.querydsl; +import static com.dnd.accompany.domain.accompany.api.dto.FindSlicesResult.*; +import static com.dnd.accompany.domain.accompany.api.dto.PageRequest.*; import static com.dnd.accompany.domain.accompany.entity.QAccompanyBoard.*; import static com.dnd.accompany.domain.accompany.entity.QAccompanyImage.*; +import static com.dnd.accompany.domain.accompany.entity.QAccompanyTag.*; import static com.dnd.accompany.domain.accompany.entity.QAccompanyUser.*; +import static com.dnd.accompany.domain.accompany.entity.enums.Region.*; import static com.dnd.accompany.domain.accompany.entity.enums.Role.*; import static com.dnd.accompany.domain.user.entity.QUser.*; @@ -20,6 +24,7 @@ import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.Expressions; +import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; @@ -30,21 +35,41 @@ public class AccompanyBoardRepositoryImpl implements AccompanyBoardRepositoryCus private final JPAQueryFactory queryFactory; - private BooleanExpression isHost() { - return accompanyUser.role.eq(HOST); - } - - private BooleanBuilder isRegion(Region region) { - BooleanBuilder clause = new BooleanBuilder(); - if (region != null) { - clause.and(accompanyBoard.region.eq(region)); - } + @Override + public Slice findBoardThumbnailsByKeyword(String cursor, int size, String keyword) { + List content = queryFactory + .select(Projections.constructor(FindBoardThumbnailsResult.class, + accompanyBoard.id, + accompanyBoard.title, + accompanyBoard.region, + accompanyBoard.startDate, + accompanyBoard.endDate, + user.nickname, + Expressions.stringTemplate("GROUP_CONCAT(DISTINCT {0})", accompanyImage.imageUrl), + Expressions.stringTemplate( + "CONCAT(DATE_FORMAT({0}, '%Y%m%d%H%i%S'), LPAD(CAST({1} AS STRING), 6, '0'))", + accompanyBoard.updatedAt, + accompanyBoard.id + )) + ) + .from(accompanyUser) + .join(accompanyUser.accompanyBoard, accompanyBoard) + .join(accompanyUser.user, user) + .leftJoin(accompanyImage).on(accompanyImage.accompanyBoard.id.eq(accompanyBoard.id)) + .where(isHost()) + .where(isContains(keyword)) + .where(cursorCondition(cursor, accompanyBoard.updatedAt, accompanyBoard.id)) + .groupBy(accompanyBoard.id, accompanyBoard.title, accompanyBoard.region, + accompanyBoard.startDate, accompanyBoard.endDate, user.nickname) + .orderBy(accompanyBoard.updatedAt.desc(), accompanyBoard.id.desc()) + .limit(size + 1) + .fetch(); - return clause; + return createSlice(size, content); } @Override - public Slice findBoardThumbnails(Pageable pageable, Region region) { + public Slice findBoardThumbnails(String cursor, int size, Region region) { List content = queryFactory .select(Projections.constructor(FindBoardThumbnailsResult.class, accompanyBoard.id, @@ -53,31 +78,31 @@ public Slice findBoardThumbnails(Pageable pageable, R accompanyBoard.startDate, accompanyBoard.endDate, user.nickname, - Expressions.stringTemplate("GROUP_CONCAT(DISTINCT {0})", accompanyImage.imageUrl))) + Expressions.stringTemplate("GROUP_CONCAT(DISTINCT {0})", accompanyImage.imageUrl), + Expressions.stringTemplate( + "CONCAT(DATE_FORMAT({0}, '%Y%m%d%H%i%S'), LPAD(CAST({1} AS STRING), 6, '0'))", + accompanyBoard.updatedAt, + accompanyBoard.id + )) + ) .from(accompanyUser) .join(accompanyUser.accompanyBoard, accompanyBoard) .join(accompanyUser.user, user) .leftJoin(accompanyImage).on(accompanyImage.accompanyBoard.id.eq(accompanyBoard.id)) .where(isHost()) .where(isRegion(region)) + .where(cursorCondition(cursor, accompanyBoard.updatedAt, accompanyBoard.id)) .groupBy(accompanyBoard.id, accompanyBoard.title, accompanyBoard.region, accompanyBoard.startDate, accompanyBoard.endDate, user.nickname) - .orderBy(accompanyBoard.updatedAt.desc(), accompanyBoard.createdAt.desc()) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize() + 1) + .orderBy(accompanyBoard.updatedAt.desc(), accompanyBoard.id.desc()) + .limit(size + 1) .fetch(); - boolean hasNext = content.size() > pageable.getPageSize(); - - if (hasNext) { - content.remove(content.size() - 1); - } - - return new SliceImpl<>(content, pageable, hasNext); + return createSlice(size, content); } @Override - public Slice findBoardThumbnailsByUserId(Pageable pageable, Long userId) { + public Slice findBoardThumbnailsByUserId(String cursor, int size, Long userId) { List content = queryFactory .select(Projections.constructor(FindBoardThumbnailsResult.class, accompanyBoard.id, @@ -86,27 +111,27 @@ public Slice findBoardThumbnailsByUserId(Pageable pag accompanyBoard.startDate, accompanyBoard.endDate, user.nickname, - Expressions.stringTemplate("GROUP_CONCAT(DISTINCT {0})", accompanyImage.imageUrl))) + Expressions.stringTemplate("GROUP_CONCAT(DISTINCT {0})", accompanyImage.imageUrl), + Expressions.stringTemplate( + "CONCAT(DATE_FORMAT({0}, '%Y%m%d%H%i%S'), LPAD(CAST({1} AS STRING), 6, '0'))", + accompanyUser.updatedAt, + accompanyUser.id + )) + ) .from(accompanyUser) .join(accompanyUser.accompanyBoard, accompanyBoard) .join(accompanyUser.user, user) .leftJoin(accompanyImage).on(accompanyImage.accompanyBoard.id.eq(accompanyBoard.id)) .where(accompanyUser.user.id.eq(userId)) + .where(cursorCondition(cursor, accompanyUser.updatedAt, accompanyUser.id)) .groupBy(accompanyBoard.id, accompanyBoard.title, accompanyBoard.region, accompanyBoard.startDate, accompanyBoard.endDate, user.nickname, accompanyUser.id) - .orderBy(accompanyUser.createdAt.desc()) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize() + 1) + .orderBy(accompanyUser.updatedAt.desc(), accompanyUser.id.desc()) + .limit(size + 1) .fetch(); - boolean hasNext = content.size() > pageable.getPageSize(); - - if (hasNext) { - content.remove(content.size() - 1); - } - - return new SliceImpl<>(content, pageable, hasNext); + return createSlice(size, content); } @Override @@ -122,4 +147,39 @@ public boolean isHostOfBoard(Long userId, Long boardId) { return fetchCount != null; } + + private BooleanExpression isHost() { + return accompanyUser.role.eq(HOST); + } + + private BooleanBuilder isRegion(Region region) { + BooleanBuilder clause = new BooleanBuilder(); + if (region != null) { + clause.and(accompanyBoard.region.eq(region)); + } + + return clause; + } + + public BooleanExpression isRegionKeyword(String keyword) { + if(from(keyword) == null) + return accompanyBoard.region.isNull(); + + return accompanyBoard.region.eq(from(keyword)); + } + + private BooleanBuilder isContains(String keyword) { + BooleanBuilder booleanBuilder = new BooleanBuilder(); + booleanBuilder.or(isRegionKeyword(keyword)); + booleanBuilder.or(accompanyBoard.title.contains(keyword)); + booleanBuilder.or( + JPAExpressions.selectOne() + .from(accompanyTag) + .where(accompanyTag.accompanyBoard.id.eq(accompanyBoard.id) + .and(accompanyTag.name.contains(keyword))) + .exists() + ); + + return booleanBuilder; + } } diff --git a/src/main/java/com/dnd/accompany/domain/accompany/infrastructure/querydsl/AccompanyRequestRepositoryImpl.java b/src/main/java/com/dnd/accompany/domain/accompany/infrastructure/querydsl/AccompanyRequestRepositoryImpl.java index 0d8a23d..6e0b572 100644 --- a/src/main/java/com/dnd/accompany/domain/accompany/infrastructure/querydsl/AccompanyRequestRepositoryImpl.java +++ b/src/main/java/com/dnd/accompany/domain/accompany/infrastructure/querydsl/AccompanyRequestRepositoryImpl.java @@ -1,5 +1,7 @@ package com.dnd.accompany.domain.accompany.infrastructure.querydsl; +import static com.dnd.accompany.domain.accompany.api.dto.FindSlicesResult.*; +import static com.dnd.accompany.domain.accompany.api.dto.PageRequest.*; import static com.dnd.accompany.domain.accompany.entity.QAccompanyBoard.*; import static com.dnd.accompany.domain.accompany.entity.QAccompanyImage.*; import static com.dnd.accompany.domain.accompany.entity.QAccompanyRequest.*; @@ -37,7 +39,7 @@ private BooleanExpression isHost() { } @Override - public Slice findBoardThumbnails(Pageable pageable, Long applicantId) { + public Slice findBoardThumbnails(String cursor, int size, Long applicantId) { List content = queryFactory .select(Projections.constructor(FindBoardThumbnailsResult.class, accompanyRequest.id, @@ -46,7 +48,13 @@ public Slice findBoardThumbnails(Pageable pageable, L accompanyBoard.startDate, accompanyBoard.endDate, user.nickname, - Expressions.stringTemplate("GROUP_CONCAT(DISTINCT {0})", accompanyImage.imageUrl))) + Expressions.stringTemplate("GROUP_CONCAT(DISTINCT {0})", accompanyImage.imageUrl), + Expressions.stringTemplate( + "CONCAT(DATE_FORMAT({0}, '%Y%m%d%H%i%S'), LPAD(CAST({1} AS STRING), 6, '0'))", + accompanyRequest.updatedAt, + accompanyRequest.id + )) + ) .from(accompanyUser) .join(accompanyUser.accompanyBoard, accompanyBoard) .join(accompanyUser.user, user) @@ -55,24 +63,18 @@ public Slice findBoardThumbnails(Pageable pageable, L .where(isHost()) .where(accompanyRequest.user.id.eq(applicantId)) .where(accompanyRequest.requestState.eq(HOLDING)) + .where(cursorCondition(cursor, accompanyRequest.updatedAt, accompanyRequest.id)) .groupBy(accompanyRequest.id, accompanyBoard.title, accompanyBoard.region, accompanyBoard.startDate, accompanyBoard.endDate, user.nickname) - .orderBy(accompanyRequest.updatedAt.desc(), accompanyRequest.createdAt.desc()) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize() + 1) + .orderBy(accompanyRequest.updatedAt.desc(), accompanyRequest.id.desc()) + .limit(size + 1) .fetch(); - boolean hasNext = content.size() > pageable.getPageSize(); - - if (hasNext) { - content.remove(content.size() - 1); - } - - return new SliceImpl<>(content, pageable, hasNext); + return createSlice(size, content); } @Override - public Slice findApplicantDetails(Pageable pageable, Long hostId) { + public Slice findApplicantDetails(String cursor, int size, Long hostId) { List content = queryFactory .select(Projections.constructor(FindApplicantDetailsResult.class, accompanyRequest.id, @@ -80,7 +82,13 @@ public Slice findApplicantDetails(Pageable pageable, user.nickname, user.provider, user.profileImageUrl, - Expressions.stringTemplate("GROUP_CONCAT(DISTINCT {0})", userImage.imageUrl))) + Expressions.stringTemplate("GROUP_CONCAT(DISTINCT {0})", userImage.imageUrl), + Expressions.stringTemplate( + "CONCAT(DATE_FORMAT({0}, '%Y%m%d%H%i%S'), LPAD(CAST({1} AS STRING), 6, '0'))", + accompanyRequest.updatedAt, + accompanyRequest.id + )) + ) .from(accompanyUser) .join(accompanyUser.accompanyBoard, accompanyBoard) .join(accompanyRequest).on(accompanyRequest.accompanyBoard.id.eq(accompanyBoard.id)) @@ -89,19 +97,13 @@ public Slice findApplicantDetails(Pageable pageable, .where(isHost()) .where(accompanyUser.user.id.eq(hostId)) .where(accompanyRequest.requestState.eq(HOLDING)) + .where(cursorCondition(cursor, accompanyRequest.updatedAt, accompanyRequest.id)) .groupBy(accompanyRequest.id, user.id, user.nickname, user.provider, user.profileImageUrl) - .orderBy(accompanyRequest.updatedAt.desc(), accompanyRequest.createdAt.desc()) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize() + 1) + .orderBy(accompanyRequest.updatedAt.desc(), accompanyRequest.id.desc()) + .limit(size + 1) .fetch(); - boolean hasNext = content.size() > pageable.getPageSize(); - - if (hasNext) { - content.remove(content.size() - 1); - } - - return new SliceImpl<>(content, pageable, hasNext); + return createSlice(size, content); } } diff --git a/src/main/java/com/dnd/accompany/domain/accompany/infrastructure/querydsl/interfaces/AccompanyBoardRepositoryCustom.java b/src/main/java/com/dnd/accompany/domain/accompany/infrastructure/querydsl/interfaces/AccompanyBoardRepositoryCustom.java index 6c11be9..310ec18 100644 --- a/src/main/java/com/dnd/accompany/domain/accompany/infrastructure/querydsl/interfaces/AccompanyBoardRepositoryCustom.java +++ b/src/main/java/com/dnd/accompany/domain/accompany/infrastructure/querydsl/interfaces/AccompanyBoardRepositoryCustom.java @@ -7,9 +7,11 @@ import com.dnd.accompany.domain.accompany.entity.enums.Region; public interface AccompanyBoardRepositoryCustom { - Slice findBoardThumbnails(Pageable pageable, Region region); + Slice findBoardThumbnailsByKeyword(String cursor, int size, String keyword); - Slice findBoardThumbnailsByUserId(Pageable pageable, Long userId); + Slice findBoardThumbnails(String cursor, int size, Region region); + + Slice findBoardThumbnailsByUserId(String cursor, int size, Long userId); boolean isHostOfBoard(Long userId, Long boardId); } diff --git a/src/main/java/com/dnd/accompany/domain/accompany/infrastructure/querydsl/interfaces/AccompanyRequestRepositoryCustom.java b/src/main/java/com/dnd/accompany/domain/accompany/infrastructure/querydsl/interfaces/AccompanyRequestRepositoryCustom.java index 46ae14b..78eb92e 100644 --- a/src/main/java/com/dnd/accompany/domain/accompany/infrastructure/querydsl/interfaces/AccompanyRequestRepositoryCustom.java +++ b/src/main/java/com/dnd/accompany/domain/accompany/infrastructure/querydsl/interfaces/AccompanyRequestRepositoryCustom.java @@ -7,7 +7,7 @@ import com.dnd.accompany.domain.accompany.api.dto.FindApplicantDetailsResult; public interface AccompanyRequestRepositoryCustom { - Slice findBoardThumbnails(Pageable pageable, Long applicantId); + Slice findBoardThumbnails(String cursor, int size, Long applicantId); - Slice findApplicantDetails(Pageable pageable, Long hostId); + Slice findApplicantDetails(String cursor, int size, Long hostId); } diff --git a/src/main/java/com/dnd/accompany/domain/accompany/service/AccompanyBoardService.java b/src/main/java/com/dnd/accompany/domain/accompany/service/AccompanyBoardService.java index f8cb6ea..16020ba 100644 --- a/src/main/java/com/dnd/accompany/domain/accompany/service/AccompanyBoardService.java +++ b/src/main/java/com/dnd/accompany/domain/accompany/service/AccompanyBoardService.java @@ -1,9 +1,9 @@ package com.dnd.accompany.domain.accompany.service; +import static com.dnd.accompany.domain.accompany.api.dto.FindBoardThumbnailsResult.*; import static com.dnd.accompany.domain.accompany.entity.AccompanyBoard.*; import java.util.List; -import java.util.Optional; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -14,6 +14,7 @@ import com.dnd.accompany.domain.accompany.api.dto.CreateAccompanyBoardRequest; import com.dnd.accompany.domain.accompany.api.dto.FindBoardThumbnailResult; import com.dnd.accompany.domain.accompany.api.dto.FindBoardThumbnailsResult; +import com.dnd.accompany.domain.accompany.api.dto.PageRequest; import com.dnd.accompany.domain.accompany.api.dto.PageResponse; import com.dnd.accompany.domain.accompany.entity.AccompanyBoard; import com.dnd.accompany.domain.accompany.entity.enums.Region; @@ -32,7 +33,7 @@ public class AccompanyBoardService { @Transactional public AccompanyBoard save(CreateAccompanyBoardRequest request) { return accompanyBoardRepository.save( - builder() + AccompanyBoard.builder() .title(request.title()) .content(request.content()) .region(request.region()) @@ -49,21 +50,29 @@ public AccompanyBoard save(CreateAccompanyBoardRequest request) { } @Transactional(readOnly = true) - public PageResponse getAllBoards(Pageable pageable, Region region) { - Slice sliceResult = accompanyBoardRepository.findBoardThumbnails(pageable, region); + public PageResponse getMatchedBoards(PageRequest request, String keyword) { + Slice sliceResult = accompanyBoardRepository.findBoardThumbnailsByKeyword(request.cursor(), request.size(), keyword); List thumbnails = getBoardThumbnails(sliceResult.getContent()); - return new PageResponse<>(sliceResult.hasNext(), thumbnails); + return new PageResponse<>(sliceResult.hasNext(), thumbnails, getLastCursor(sliceResult.getContent())); + } + + public PageResponse getAllBoards(PageRequest request, Region region) { + Slice sliceResult = accompanyBoardRepository.findBoardThumbnails(request.cursor(), request.size(), region); + + List thumbnails = getBoardThumbnails(sliceResult.getContent()); + + return new PageResponse<>(sliceResult.hasNext(), thumbnails, getLastCursor(sliceResult.getContent())); } @Transactional(readOnly = true) - public PageResponse getAllRecords(Pageable pageable, Long userId) { - Slice sliceResult = accompanyBoardRepository.findBoardThumbnailsByUserId(pageable, userId); + public PageResponse getAllRecords(PageRequest request, Long userId) { + Slice sliceResult = accompanyBoardRepository.findBoardThumbnailsByUserId(request.cursor(), request.size(), userId); List thumbnails = getBoardThumbnails(sliceResult.getContent()); - return new PageResponse<>(sliceResult.hasNext(), thumbnails); + return new PageResponse<>(sliceResult.hasNext(), thumbnails, getLastCursor(sliceResult.getContent())); } @@ -73,12 +82,12 @@ public PageResponse getAllRecords(Pageable pageable, Lo private List getBoardThumbnails(List results) { List thumbnails = results.stream() .map(result -> AccompanyBoardThumbnail.builder() - .boardId(result.requestId()) - .title(result.title()) - .region(result.region()) - .startDate(result.startDate()) - .endDate(result.endDate()) - .nickname(result.nickname()) + .boardId(result.getRequestId()) + .title(result.getTitle()) + .region(result.getRegion()) + .startDate(result.getStartDate()) + .endDate(result.getEndDate()) + .nickname(result.getNickname()) .imageUrls(result.getImageUrlsAsList()) .build()) .toList(); diff --git a/src/main/java/com/dnd/accompany/domain/accompany/service/AccompanyRequestService.java b/src/main/java/com/dnd/accompany/domain/accompany/service/AccompanyRequestService.java index b7c2dae..092a7c9 100644 --- a/src/main/java/com/dnd/accompany/domain/accompany/service/AccompanyRequestService.java +++ b/src/main/java/com/dnd/accompany/domain/accompany/service/AccompanyRequestService.java @@ -1,9 +1,9 @@ package com.dnd.accompany.domain.accompany.service; +import static com.dnd.accompany.domain.accompany.api.dto.FindApplicantDetailsResult.*; import static com.dnd.accompany.domain.accompany.entity.enums.RequestState.*; import static java.util.stream.Collectors.*; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; @@ -18,6 +18,8 @@ import com.dnd.accompany.domain.accompany.api.dto.CreateAccompanyRequest; import com.dnd.accompany.domain.accompany.api.dto.FindBoardThumbnailsResult; import com.dnd.accompany.domain.accompany.api.dto.FindApplicantDetailsResult; +import com.dnd.accompany.domain.accompany.api.dto.FindSlicesResult; +import com.dnd.accompany.domain.accompany.api.dto.PageRequest; import com.dnd.accompany.domain.accompany.api.dto.PageResponse; import com.dnd.accompany.domain.accompany.api.dto.ReceivedAccompany; import com.dnd.accompany.domain.accompany.api.dto.SendedAccompany; @@ -52,20 +54,20 @@ public void save(Long userId, CreateAccompanyRequest request) { } @Transactional(readOnly = true) - public PageResponse getAllReceivedAccompanies(Pageable pageable, Long hostId){ - Slice sliceResult = accompanyRequestRepository.findApplicantDetails(pageable, hostId); + public PageResponse getAllReceivedAccompanies(PageRequest request, Long hostId){ + Slice sliceResult = accompanyRequestRepository.findApplicantDetails(request.cursor(), request.size(), hostId); Set userIds = getUserIds(sliceResult); Map userProfileMap = getUserProfileMap(userIds); List receivedAccompanies = getReceivedAccompanies(sliceResult.getContent(), userProfileMap); - return new PageResponse<>(sliceResult.hasNext(), receivedAccompanies); + return new PageResponse<>(sliceResult.hasNext(), receivedAccompanies, FindSlicesResult.getLastCursor(sliceResult.getContent())); } private Set getUserIds(Slice results) { return results.getContent().stream() - .map(result -> result.userId()) + .map(result -> result.getUserId()) .collect(toSet()); } @@ -80,14 +82,14 @@ private Map getUserProfileMap(Set userIds) { private static List getReceivedAccompanies(List results, Map userProfileMap) { return results.stream() .map(result -> { - UserProfile userProfile = userProfileMap.get(result.userId()); + UserProfile userProfile = userProfileMap.get(result.getUserId()); return ReceivedAccompany.builder() - .requestId(result.requestId()) - .userId(result.userId()) - .nickname(result.nickname()) - .provider(result.provider()) - .profileImageUrl(result.profileImageUrl()) + .requestId(result.getRequestId()) + .userId(result.getUserId()) + .nickname(result.getNickname()) + .provider(result.getProvider()) + .profileImageUrl(result.getProfileImageUrl()) .description(userProfile.getDescription()) .gender(userProfile.getGender()) .birthYear(userProfile.getBirthYear()) @@ -103,12 +105,12 @@ private static List getReceivedAccompanies(List getAllSendedAccompanies(Pageable pageable, Long applicantId){ - Slice sliceResult = accompanyRequestRepository.findBoardThumbnails(pageable, applicantId); + public PageResponse getAllSendedAccompanies(PageRequest request, Long applicantId){ + Slice sliceResult = accompanyRequestRepository.findBoardThumbnails(request.cursor(), request.size(), applicantId); List sendedAccompanies = getSendedAccompanies(sliceResult.getContent()); - return new PageResponse<>(sliceResult.hasNext(), sendedAccompanies); + return new PageResponse<>(sliceResult.hasNext(), sendedAccompanies, getLastCursor(sliceResult.getContent())); } /** @@ -117,12 +119,12 @@ public PageResponse getAllSendedAccompanies(Pageable pageable, private static List getSendedAccompanies(List results) { List sendedAccompanies = results.stream() .map(result -> SendedAccompany.builder() - .requestId(result.requestId()) - .title(result.title()) - .region(result.region()) - .startDate(result.startDate()) - .endDate(result.endDate()) - .nickname(result.nickname()) + .requestId(result.getRequestId()) + .title(result.getTitle()) + .region(result.getRegion()) + .startDate(result.getStartDate()) + .endDate(result.getEndDate()) + .nickname(result.getNickname()) .imageUrls(result.getImageUrlsAsList()) .build()) .toList(); From 80f5f002f97a404625ca6043edbb4010eb7532c5 Mon Sep 17 00:00:00 2001 From: rlarltj Date: Sat, 31 Aug 2024 16:41:02 +0900 Subject: [PATCH 2/3] =?UTF-8?q?refactor:=20=EB=8F=99=ED=96=89=20=ED=8F=89?= =?UTF-8?q?=EA=B0=80=20api=20=EC=9D=91=EB=8B=B5=EC=97=90=20=EA=B0=81?= =?UTF-8?q?=EA=B0=81=EC=9D=98=20count=EB=A5=BC=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/dto/SimpleEvaluationResponse.java | 15 +++-- .../api/dto/SimpleEvaluationResult.java | 13 ++-- .../review/api/dto/TypeCountResult.java | 13 ++++ .../infrastructure/ReviewRepositoryImpl.java | 63 ++++++++++--------- .../domain/review/service/ReviewService.java | 30 +++++++-- 5 files changed, 87 insertions(+), 47 deletions(-) create mode 100644 src/main/java/com/dnd/accompany/domain/review/api/dto/TypeCountResult.java diff --git a/src/main/java/com/dnd/accompany/domain/review/api/dto/SimpleEvaluationResponse.java b/src/main/java/com/dnd/accompany/domain/review/api/dto/SimpleEvaluationResponse.java index e1bc9c9..55194a9 100644 --- a/src/main/java/com/dnd/accompany/domain/review/api/dto/SimpleEvaluationResponse.java +++ b/src/main/java/com/dnd/accompany/domain/review/api/dto/SimpleEvaluationResponse.java @@ -1,15 +1,18 @@ package com.dnd.accompany.domain.review.api.dto; -import com.dnd.accompany.domain.review.entity.enums.PersonalityType; -import com.dnd.accompany.domain.review.entity.enums.TravelPreferenceType; -import com.dnd.accompany.domain.review.entity.enums.TravelStyleType; import lombok.Builder; @Builder public record SimpleEvaluationResponse( - TravelStyleType travelStyle, - TravelPreferenceType travelPreference, - PersonalityType personalityType, + String travelStyle, + long travelStyleCount, + + String travelPreference, + long travelPreferenceCount, + + String personalityType, + long personalityTypeCount, + int evaluationCount ) { } diff --git a/src/main/java/com/dnd/accompany/domain/review/api/dto/SimpleEvaluationResult.java b/src/main/java/com/dnd/accompany/domain/review/api/dto/SimpleEvaluationResult.java index 249871c..113ba14 100644 --- a/src/main/java/com/dnd/accompany/domain/review/api/dto/SimpleEvaluationResult.java +++ b/src/main/java/com/dnd/accompany/domain/review/api/dto/SimpleEvaluationResult.java @@ -1,17 +1,16 @@ package com.dnd.accompany.domain.review.api.dto; -import com.dnd.accompany.domain.review.entity.enums.PersonalityType; -import com.dnd.accompany.domain.review.entity.enums.TravelPreferenceType; -import com.dnd.accompany.domain.review.entity.enums.TravelStyleType; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; +import java.util.List; + @Getter @NoArgsConstructor @AllArgsConstructor public class SimpleEvaluationResult { - private TravelStyleType travelStyle; - private TravelPreferenceType travelPreference; - private PersonalityType personalityType; -} + private List travelStyles; + private List travelPreferences; + private List personalityTypes; +} \ No newline at end of file diff --git a/src/main/java/com/dnd/accompany/domain/review/api/dto/TypeCountResult.java b/src/main/java/com/dnd/accompany/domain/review/api/dto/TypeCountResult.java new file mode 100644 index 0000000..265f1a9 --- /dev/null +++ b/src/main/java/com/dnd/accompany/domain/review/api/dto/TypeCountResult.java @@ -0,0 +1,13 @@ +package com.dnd.accompany.domain.review.api.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class TypeCountResult { + private String type; + private long count; +} \ No newline at end of file diff --git a/src/main/java/com/dnd/accompany/domain/review/infrastructure/ReviewRepositoryImpl.java b/src/main/java/com/dnd/accompany/domain/review/infrastructure/ReviewRepositoryImpl.java index cc4af75..b37c115 100644 --- a/src/main/java/com/dnd/accompany/domain/review/infrastructure/ReviewRepositoryImpl.java +++ b/src/main/java/com/dnd/accompany/domain/review/infrastructure/ReviewRepositoryImpl.java @@ -2,9 +2,9 @@ import com.dnd.accompany.domain.review.api.dto.SimpleEvaluationResult; import com.dnd.accompany.domain.review.api.dto.SimpleReviewResult; +import com.dnd.accompany.domain.review.api.dto.TypeCountResult; import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.BooleanExpression; -import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; @@ -17,7 +17,6 @@ import static com.dnd.accompany.domain.review.entity.QTravelPreference.travelPreference; import static com.dnd.accompany.domain.review.entity.QTravelStyle.travelStyle; import static com.dnd.accompany.domain.user.entity.QUser.user; -import static com.querydsl.jpa.JPAExpressions.*; @Repository @RequiredArgsConstructor @@ -47,36 +46,40 @@ public List findAllByReceiverId(Long receiverId) { @Override public SimpleEvaluationResult findEvaluationsByReceiverId(Long userId) { - return jpaQueryFactory - .select( - Projections.constructor(SimpleEvaluationResult.class, - select(travelStyle.type) - .from(review) - .join(review.travelStyle, travelStyle) - .where(eqReceiver(userId)) - .groupBy(travelStyle.type) - .orderBy(travelStyle.type.count().desc()) - .limit(1), + List travelStyleCounts = jpaQueryFactory + .select(Projections.constructor(TypeCountResult.class, + travelStyle.type.stringValue(), + travelStyle.type.count() + )) + .from(travelStyle) + .join(travelStyle.review, review) + .where(eqReceiver(userId)) + .groupBy(travelStyle.type) + .fetch(); - select(travelPreference.type) - .from(review) - .join(review.travelPreference, travelPreference) - .where(eqReceiver(userId)) - .groupBy(travelPreference.type) - .orderBy(travelPreference.type.count().desc()) - .limit(1), + List travelPreferenceCounts = jpaQueryFactory + .select(Projections.constructor(TypeCountResult.class, + travelPreference.type.stringValue(), + travelPreference.type.count() + )) + .from(travelPreference) + .join(travelPreference.review, review) + .where(eqReceiver(userId)) + .groupBy(travelPreference.type) + .fetch(); - select(personality.type) - .from(review) - .join(review.personalityType, personality) - .where(eqReceiver(userId)) - .groupBy(personality.type) - .orderBy(personality.type.count().desc()) - .limit(1) - ) - ) - .from(review) - .fetchFirst(); + List personalityCounts = jpaQueryFactory + .select(Projections.constructor(TypeCountResult.class, + personality.type.stringValue(), + personality.type.count() + )) + .from(personality) + .join(personality.review, review) + .where(eqReceiver(userId)) + .groupBy(personality.type) + .fetch(); + + return new SimpleEvaluationResult(travelStyleCounts, travelPreferenceCounts, personalityCounts); } private BooleanExpression eqReceiver(Long receiverId) { diff --git a/src/main/java/com/dnd/accompany/domain/review/service/ReviewService.java b/src/main/java/com/dnd/accompany/domain/review/service/ReviewService.java index 365f221..72bc807 100644 --- a/src/main/java/com/dnd/accompany/domain/review/service/ReviewService.java +++ b/src/main/java/com/dnd/accompany/domain/review/service/ReviewService.java @@ -8,7 +8,9 @@ import com.dnd.accompany.domain.review.api.dto.SimpleEvaluationResult; import com.dnd.accompany.domain.review.api.dto.SimpleReviewResponses; import com.dnd.accompany.domain.review.api.dto.SimpleReviewResult; +import com.dnd.accompany.domain.review.api.dto.TypeCountResult; import com.dnd.accompany.domain.review.entity.Review; +import com.dnd.accompany.domain.review.entity.enums.TravelStyleType; import com.dnd.accompany.domain.review.infrastructure.ReviewRepository; import com.dnd.accompany.domain.user.entity.User; import com.dnd.accompany.domain.user.infrastructure.UserRepository; @@ -19,6 +21,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.Comparator; import java.util.List; @Service @@ -97,13 +100,32 @@ private void validateReceiver(Long userId, Review review) { public SimpleEvaluationResponse getEvaluation(Long userId) { User receiver = getUser(userId); - SimpleEvaluationResult result = reviewRepository.findEvaluationsByReceiverId(userId); + SimpleEvaluationResult evaluations = getEvaluations(userId); + + TypeCountResult topTravelStyle = evaluations.getTravelStyles().stream() + .max(Comparator.comparingLong(TypeCountResult::getCount)) + .orElse(null); + + TypeCountResult topTravelPreference = evaluations.getTravelPreferences().stream() + .max(Comparator.comparingLong(TypeCountResult::getCount)) + .orElse(null); + + TypeCountResult topPersonalityType = evaluations.getPersonalityTypes().stream() + .max(Comparator.comparingLong(TypeCountResult::getCount)) + .orElse(null); return SimpleEvaluationResponse.builder() - .personalityType(result.getPersonalityType()) - .travelPreference(result.getTravelPreference()) - .travelStyle(result.getTravelStyle()) + .travelStyle(topTravelStyle != null ? topTravelStyle.getType() : null) + .travelStyleCount(topTravelStyle != null ? topTravelStyle.getCount() : 0) + .travelPreference(topTravelPreference != null ? topTravelPreference.getType() : null) + .travelPreferenceCount(topTravelPreference != null ? topTravelPreference.getCount() : 0) + .personalityType(topPersonalityType != null ? topPersonalityType.getType() : null) + .personalityTypeCount(topPersonalityType != null ? topPersonalityType.getCount() : 0) .evaluationCount(receiver.getEvaluationCount()) .build(); } + + private SimpleEvaluationResult getEvaluations(Long userId) { + return reviewRepository.findEvaluationsByReceiverId(userId); + } } From e766fb6df8d0ad8c459aa3ad2e2202e52a165720 Mon Sep 17 00:00:00 2001 From: rlarltj Date: Sat, 31 Aug 2024 17:19:10 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20=EB=B0=9B=EC=9D=80=20=EB=8F=99?= =?UTF-8?q?=ED=96=89=20=ED=8F=89=EA=B0=80=EB=82=B4=EC=97=AD=EC=9D=84=20?= =?UTF-8?q?=EC=A0=84=EB=B6=80=20=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8A=94=20api?= =?UTF-8?q?=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/review/api/ReviewController.java | 14 +++- .../api/dto/AllEvaluationResponses.java | 12 ++++ .../review/api/dto/EvaluationResponse.java | 31 +++++++++ .../api/dto/SimpleEvaluationResponse.java | 12 +--- .../domain/review/service/ReviewService.java | 64 ++++++++++++++----- 5 files changed, 105 insertions(+), 28 deletions(-) create mode 100644 src/main/java/com/dnd/accompany/domain/review/api/dto/AllEvaluationResponses.java create mode 100644 src/main/java/com/dnd/accompany/domain/review/api/dto/EvaluationResponse.java diff --git a/src/main/java/com/dnd/accompany/domain/review/api/ReviewController.java b/src/main/java/com/dnd/accompany/domain/review/api/ReviewController.java index 367abaa..0ef4d66 100644 --- a/src/main/java/com/dnd/accompany/domain/review/api/ReviewController.java +++ b/src/main/java/com/dnd/accompany/domain/review/api/ReviewController.java @@ -1,6 +1,7 @@ package com.dnd.accompany.domain.review.api; import com.dnd.accompany.domain.auth.dto.jwt.JwtAuthentication; +import com.dnd.accompany.domain.review.api.dto.AllEvaluationResponses; import com.dnd.accompany.domain.review.api.dto.CreateReviewRequest; import com.dnd.accompany.domain.review.api.dto.ReviewDetailsResponse; import com.dnd.accompany.domain.review.api.dto.ReviewDetailsResult; @@ -63,9 +64,18 @@ public ResponseEntity getReviewList( @Operation(summary = "받은 동행 평가 조회") @GetMapping("/evaluations") public ResponseEntity getEvaluation( - @AuthenticationPrincipal JwtAuthentication user +// @AuthenticationPrincipal JwtAuthentication user + ) { + SimpleEvaluationResponse response = reviewService.getSimpleEvaluation(3L); + return ResponseEntity.ok(response); + } + + @Operation(summary = "받은 모든 동행 평가 조회") + @GetMapping("/evaluations/all") + public ResponseEntity getEvaluations( +// @AuthenticationPrincipal JwtAuthentication user ) { - SimpleEvaluationResponse response = reviewService.getEvaluation(user.getId()); + AllEvaluationResponses response = reviewService.getEvaluation(3L); return ResponseEntity.ok(response); } } diff --git a/src/main/java/com/dnd/accompany/domain/review/api/dto/AllEvaluationResponses.java b/src/main/java/com/dnd/accompany/domain/review/api/dto/AllEvaluationResponses.java new file mode 100644 index 0000000..c2d010a --- /dev/null +++ b/src/main/java/com/dnd/accompany/domain/review/api/dto/AllEvaluationResponses.java @@ -0,0 +1,12 @@ +package com.dnd.accompany.domain.review.api.dto; + +import lombok.Builder; + +import java.util.List; + +@Builder +public record AllEvaluationResponses ( + List evaluationResponse, + int evaluationCount +){ +} diff --git a/src/main/java/com/dnd/accompany/domain/review/api/dto/EvaluationResponse.java b/src/main/java/com/dnd/accompany/domain/review/api/dto/EvaluationResponse.java new file mode 100644 index 0000000..1cb947d --- /dev/null +++ b/src/main/java/com/dnd/accompany/domain/review/api/dto/EvaluationResponse.java @@ -0,0 +1,31 @@ +package com.dnd.accompany.domain.review.api.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; + +@AllArgsConstructor +@Builder +public class EvaluationResponse { + @JsonProperty("type") + private String type; + + @JsonProperty("count") + private long count; + + public static EvaluationResponse create(TypeCountResult topResult) { + if (topResult != null) { + return EvaluationResponse.builder() + .type(topResult.getType() != null ? topResult.getType() : null) + .count(topResult.getCount()) + .build(); + } else { + return EvaluationResponse.builder() + .type(null) + .count(0) + .build(); + } + } +} + + diff --git a/src/main/java/com/dnd/accompany/domain/review/api/dto/SimpleEvaluationResponse.java b/src/main/java/com/dnd/accompany/domain/review/api/dto/SimpleEvaluationResponse.java index 55194a9..f343852 100644 --- a/src/main/java/com/dnd/accompany/domain/review/api/dto/SimpleEvaluationResponse.java +++ b/src/main/java/com/dnd/accompany/domain/review/api/dto/SimpleEvaluationResponse.java @@ -2,17 +2,11 @@ import lombok.Builder; +import java.util.List; + @Builder public record SimpleEvaluationResponse( - String travelStyle, - long travelStyleCount, - - String travelPreference, - long travelPreferenceCount, - - String personalityType, - long personalityTypeCount, - + List evaluationResponse, int evaluationCount ) { } diff --git a/src/main/java/com/dnd/accompany/domain/review/service/ReviewService.java b/src/main/java/com/dnd/accompany/domain/review/service/ReviewService.java index 72bc807..4a351d0 100644 --- a/src/main/java/com/dnd/accompany/domain/review/service/ReviewService.java +++ b/src/main/java/com/dnd/accompany/domain/review/service/ReviewService.java @@ -2,7 +2,9 @@ import com.dnd.accompany.domain.accompany.entity.AccompanyBoard; import com.dnd.accompany.domain.accompany.infrastructure.AccompanyBoardRepository; +import com.dnd.accompany.domain.review.api.dto.AllEvaluationResponses; import com.dnd.accompany.domain.review.api.dto.CreateReviewRequest; +import com.dnd.accompany.domain.review.api.dto.EvaluationResponse; import com.dnd.accompany.domain.review.api.dto.ReviewDetailsResult; import com.dnd.accompany.domain.review.api.dto.SimpleEvaluationResponse; import com.dnd.accompany.domain.review.api.dto.SimpleEvaluationResult; @@ -10,7 +12,6 @@ import com.dnd.accompany.domain.review.api.dto.SimpleReviewResult; import com.dnd.accompany.domain.review.api.dto.TypeCountResult; import com.dnd.accompany.domain.review.entity.Review; -import com.dnd.accompany.domain.review.entity.enums.TravelStyleType; import com.dnd.accompany.domain.review.infrastructure.ReviewRepository; import com.dnd.accompany.domain.user.entity.User; import com.dnd.accompany.domain.user.infrastructure.UserRepository; @@ -21,6 +22,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -98,29 +100,57 @@ private void validateReceiver(Long userId, Review review) { } } - public SimpleEvaluationResponse getEvaluation(Long userId) { + public AllEvaluationResponses getEvaluation(Long userId) { User receiver = getUser(userId); SimpleEvaluationResult evaluations = getEvaluations(userId); - TypeCountResult topTravelStyle = evaluations.getTravelStyles().stream() - .max(Comparator.comparingLong(TypeCountResult::getCount)) - .orElse(null); + List travelStyles = evaluations.getTravelStyles(); + List travelPreferences = evaluations.getTravelPreferences(); + List personalities = evaluations.getPersonalityTypes(); - TypeCountResult topTravelPreference = evaluations.getTravelPreferences().stream() - .max(Comparator.comparingLong(TypeCountResult::getCount)) - .orElse(null); + List combinedResults = new ArrayList<>(); + combinedResults.addAll(travelStyles); + combinedResults.addAll(travelPreferences); + combinedResults.addAll(personalities); - TypeCountResult topPersonalityType = evaluations.getPersonalityTypes().stream() - .max(Comparator.comparingLong(TypeCountResult::getCount)) - .orElse(null); + combinedResults.sort(Comparator.comparingLong(TypeCountResult::getCount).reversed()); + + List evaluationResponses = combinedResults.stream() + .map(EvaluationResponse::create) + .toList(); + + return AllEvaluationResponses.builder() + .evaluationResponse(evaluationResponses) + .evaluationCount(receiver.getEvaluationCount()) + .build(); + } + + public SimpleEvaluationResponse getSimpleEvaluation(Long userId) { + User receiver = getUser(userId); + SimpleEvaluationResult evaluations = getEvaluations(userId); + + List evaluationResponses = List.of( + EvaluationResponse.create( + evaluations.getTravelStyles().stream() + .max(Comparator.comparingLong(TypeCountResult::getCount)) + .orElse(null) + ), + + EvaluationResponse.create( + evaluations.getTravelPreferences().stream() + .max(Comparator.comparingLong(TypeCountResult::getCount)) + .orElse(null) + ), + + EvaluationResponse.create( + evaluations.getPersonalityTypes().stream() + .max(Comparator.comparingLong(TypeCountResult::getCount)) + .orElse(null) + ) + ); return SimpleEvaluationResponse.builder() - .travelStyle(topTravelStyle != null ? topTravelStyle.getType() : null) - .travelStyleCount(topTravelStyle != null ? topTravelStyle.getCount() : 0) - .travelPreference(topTravelPreference != null ? topTravelPreference.getType() : null) - .travelPreferenceCount(topTravelPreference != null ? topTravelPreference.getCount() : 0) - .personalityType(topPersonalityType != null ? topPersonalityType.getType() : null) - .personalityTypeCount(topPersonalityType != null ? topPersonalityType.getCount() : 0) + .evaluationResponse(evaluationResponses) .evaluationCount(receiver.getEvaluationCount()) .build(); }