diff --git a/src/main/java/life/mosu/mosuserver/application/inquiry/InquiryService.java b/src/main/java/life/mosu/mosuserver/application/inquiry/InquiryService.java index addec98a..faca75b0 100644 --- a/src/main/java/life/mosu/mosuserver/application/inquiry/InquiryService.java +++ b/src/main/java/life/mosu/mosuserver/application/inquiry/InquiryService.java @@ -10,6 +10,7 @@ import life.mosu.mosuserver.presentation.inquiry.dto.InquiryCreateRequest; import life.mosu.mosuserver.presentation.inquiry.dto.InquiryDetailResponse; import life.mosu.mosuserver.presentation.inquiry.dto.InquiryDetailResponse.InquiryAnswerDetailResponse; +import life.mosu.mosuserver.presentation.inquiry.dto.InquiryListResponse; import life.mosu.mosuserver.presentation.inquiry.dto.InquiryResponse; import life.mosu.mosuserver.presentation.inquiry.dto.InquiryUpdateRequest; import lombok.RequiredArgsConstructor; @@ -56,8 +57,9 @@ public InquiryDetailResponse getInquiryDetail(UserJpaEntity user, Long postId) { } @Transactional(readOnly = true, propagation = Propagation.SUPPORTS) - public Page getMyInquiry(Long userId, Pageable pageable) { - return inquiryJpaRepository.searchMyInquiry(userId, pageable); + public Page getMyInquiry(Long userId, Pageable pageable) { + return inquiryJpaRepository.searchMyInquiry(userId, + pageable); } @Transactional diff --git a/src/main/java/life/mosu/mosuserver/domain/inquiry/repository/InquiryQueryRepository.java b/src/main/java/life/mosu/mosuserver/domain/inquiry/repository/InquiryQueryRepository.java index f26d0943..84323823 100644 --- a/src/main/java/life/mosu/mosuserver/domain/inquiry/repository/InquiryQueryRepository.java +++ b/src/main/java/life/mosu/mosuserver/domain/inquiry/repository/InquiryQueryRepository.java @@ -1,6 +1,7 @@ package life.mosu.mosuserver.domain.inquiry.repository; import life.mosu.mosuserver.domain.inquiry.entity.InquiryStatus; +import life.mosu.mosuserver.presentation.inquiry.dto.InquiryListResponse; import life.mosu.mosuserver.presentation.inquiry.dto.InquiryResponse; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -10,5 +11,5 @@ public interface InquiryQueryRepository { Page searchInquiries(InquiryStatus status, String sortField, boolean asc, Pageable pageable); - Page searchMyInquiry(Long userId, Pageable pageable); + Page searchMyInquiry(Long userId, Pageable pageable); } diff --git a/src/main/java/life/mosu/mosuserver/infra/persistence/jpa/InquiryJpaRepositoryImpl.java b/src/main/java/life/mosu/mosuserver/infra/persistence/jpa/InquiryJpaRepositoryImpl.java index 2ac21f89..f82c96c5 100644 --- a/src/main/java/life/mosu/mosuserver/infra/persistence/jpa/InquiryJpaRepositoryImpl.java +++ b/src/main/java/life/mosu/mosuserver/infra/persistence/jpa/InquiryJpaRepositoryImpl.java @@ -15,6 +15,9 @@ import life.mosu.mosuserver.domain.inquiry.entity.InquiryStatus; import life.mosu.mosuserver.domain.inquiry.entity.QInquiryJpaEntity; import life.mosu.mosuserver.domain.inquiry.repository.InquiryQueryRepository; +import life.mosu.mosuserver.domain.inquiryAnswer.entity.QInquiryAnswerJpaEntity; +import life.mosu.mosuserver.presentation.inquiry.dto.InquiryAnswerResponse; +import life.mosu.mosuserver.presentation.inquiry.dto.InquiryListResponse; import life.mosu.mosuserver.presentation.inquiry.dto.InquiryResponse; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; @@ -29,6 +32,7 @@ public class InquiryJpaRepositoryImpl implements InquiryQueryRepository { private final JPAQueryFactory queryFactory; private final EntityManager entityManager; private final QInquiryJpaEntity inquiry = QInquiryJpaEntity.inquiryJpaEntity; + private final QInquiryAnswerJpaEntity inquiryAnswer = QInquiryAnswerJpaEntity.inquiryAnswerJpaEntity; @Override public Page searchInquiries( @@ -38,40 +42,42 @@ public Page searchInquiries( Pageable pageable ) { - JPAQuery query = baseQuery(inquiry) - .where(buildStatusCondition(inquiry, status)) - .orderBy(buildOrderByCondition(sortField, asc)) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize()); + JPAQuery query = baseQuery() + .where(buildStatusCondition(inquiry, status)); long total = getTotalCount(query, inquiry.count()); - List content = query.fetch().stream() - .map(this::mapToResponse) + List content = query + .orderBy(buildOrderByCondition(sortField, asc)) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch().stream() + .map(this::mapToInquiryResponse) .toList(); return new PageImpl<>(content, pageable, total); } @Override - public Page searchMyInquiry(Long userId, Pageable pageable) { - JPAQuery query = baseQuery(inquiry) + public Page searchMyInquiry(Long userId, Pageable pageable) { + JPAQuery query = baseQueryWithAnswer() .where(inquiry.userId.eq(userId)); - long total = getTotalCount(query, inquiry.count()); + long total = getTotalCount(query, inquiry.countDistinct()); - List content = query + List content = query + .orderBy(inquiry.createdAt.desc()) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) .fetch().stream() - .map(this::mapToResponse) + .map(this::mapToInquiryListResponse) .toList(); return new PageImpl<>(content, pageable, total); } - private JPAQuery baseQuery(QInquiryJpaEntity inquiry) { + private JPAQuery baseQuery() { return queryFactory .select( inquiry.id, @@ -85,6 +91,27 @@ private JPAQuery baseQuery(QInquiryJpaEntity inquiry) { } + private JPAQuery baseQueryWithAnswer() { + return queryFactory + .select( + inquiry.id, + inquiry.title, + inquiry.content, + inquiry.author, + inquiry.status, + inquiry.createdAt, + inquiryAnswer.id, + inquiryAnswer.title, + inquiryAnswer.content, + inquiryAnswer.author, + inquiryAnswer.createdAt, + inquiryAnswer.updatedAt + ) + .from(inquiry) + .leftJoin(inquiryAnswer) + .on(inquiryAnswer.inquiryId.eq(inquiry.id)); + } + private BooleanExpression buildStatusCondition(QInquiryJpaEntity inquiry, InquiryStatus status) { return status != null ? inquiry.status.eq(status) : null; @@ -108,7 +135,7 @@ private long getTotalCount(JPAQuery query, Expression countExpressi ).orElse(0L); } - private InquiryResponse mapToResponse(Tuple tuple) { + private InquiryResponse mapToInquiryResponse(Tuple tuple) { InquiryStatus status = tuple.get(inquiry.status); return new InquiryResponse( tuple.get(inquiry.id), @@ -119,4 +146,22 @@ private InquiryResponse mapToResponse(Tuple tuple) { formatDate(tuple.get(inquiry.createdAt)) ); } + + + private InquiryListResponse mapToInquiryListResponse(Tuple tuple) { + InquiryResponse inquiryDto = mapToInquiryResponse(tuple); + InquiryAnswerResponse answerDto = null; + + if (tuple.get(inquiryAnswer.id) != null) { + answerDto = new InquiryAnswerResponse( + tuple.get(inquiryAnswer.title), + tuple.get(inquiryAnswer.content), + tuple.get(inquiryAnswer.author), + formatDate(tuple.get(inquiryAnswer.createdAt)), + formatDate(tuple.get(inquiryAnswer.updatedAt)) + ); + } + + return InquiryListResponse.of(inquiryDto, answerDto); + } } diff --git a/src/main/java/life/mosu/mosuserver/presentation/inquiry/InquiryController.java b/src/main/java/life/mosu/mosuserver/presentation/inquiry/InquiryController.java index f9bff42d..8d2cb577 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/inquiry/InquiryController.java +++ b/src/main/java/life/mosu/mosuserver/presentation/inquiry/InquiryController.java @@ -7,7 +7,7 @@ import life.mosu.mosuserver.global.util.ApiResponseWrapper; import life.mosu.mosuserver.presentation.inquiry.dto.InquiryCreateRequest; import life.mosu.mosuserver.presentation.inquiry.dto.InquiryDetailResponse; -import life.mosu.mosuserver.presentation.inquiry.dto.InquiryResponse; +import life.mosu.mosuserver.presentation.inquiry.dto.InquiryListResponse; import life.mosu.mosuserver.presentation.inquiry.dto.InquiryUpdateRequest; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; @@ -54,12 +54,13 @@ public ResponseEntity> update( @GetMapping("/my") @PreAuthorize("isAuthenticated() and hasRole('USER')") - public ResponseEntity>> getMyInquiries( + public ResponseEntity>> getMyInquiries( @UserId Long userId, @PageableDefault(size = 10) Pageable pageable ) { - Page inquiries = inquiryService.getMyInquiry(userId, pageable); - return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.OK, "내 질문 목록 조회 성공", inquiries)); + Page inquiries = inquiryService.getMyInquiry(userId, pageable); + return ResponseEntity.ok( + ApiResponseWrapper.success(HttpStatus.OK, "내 질문 목록 조회 성공", inquiries)); } @GetMapping("/{postId}") @@ -68,7 +69,8 @@ public ResponseEntity> getInquiryDetai @AuthenticationPrincipal PrincipalDetails principalDetails, @PathVariable Long postId) { - InquiryDetailResponse inquiry = inquiryService.getInquiryDetail(principalDetails.user(), postId); + InquiryDetailResponse inquiry = inquiryService.getInquiryDetail(principalDetails.user(), + postId); return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.OK, "질문 상세 조회 성공", inquiry)); } diff --git a/src/main/java/life/mosu/mosuserver/presentation/inquiry/InquiryControllerDocs.java b/src/main/java/life/mosu/mosuserver/presentation/inquiry/InquiryControllerDocs.java index e592404c..f58ebf49 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/inquiry/InquiryControllerDocs.java +++ b/src/main/java/life/mosu/mosuserver/presentation/inquiry/InquiryControllerDocs.java @@ -13,7 +13,7 @@ import life.mosu.mosuserver.global.util.ApiResponseWrapper; import life.mosu.mosuserver.presentation.inquiry.dto.InquiryCreateRequest; import life.mosu.mosuserver.presentation.inquiry.dto.InquiryDetailResponse; -import life.mosu.mosuserver.presentation.inquiry.dto.InquiryResponse; +import life.mosu.mosuserver.presentation.inquiry.dto.InquiryListResponse; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; @@ -37,7 +37,7 @@ ResponseEntity> create( @ApiResponse(responseCode = "200", description = "내 문의글 목록 조회 성공", content = @Content(schema = @Schema(implementation = Page.class))) }) - ResponseEntity>> getMyInquiries( + ResponseEntity>> getMyInquiries( @Parameter(description = "사용자 ID", required = true) Long userId, @Parameter(description = "페이지 정보") Pageable pageable ); diff --git a/src/main/java/life/mosu/mosuserver/presentation/inquiry/dto/InquiryAnswerResponse.java b/src/main/java/life/mosu/mosuserver/presentation/inquiry/dto/InquiryAnswerResponse.java new file mode 100644 index 00000000..a6adb4ad --- /dev/null +++ b/src/main/java/life/mosu/mosuserver/presentation/inquiry/dto/InquiryAnswerResponse.java @@ -0,0 +1,11 @@ +package life.mosu.mosuserver.presentation.inquiry.dto; + +public record InquiryAnswerResponse( + String title, + String content, + String author, + String createdAt, + String updatedAt +) { + +} diff --git a/src/main/java/life/mosu/mosuserver/presentation/inquiry/dto/InquiryListResponse.java b/src/main/java/life/mosu/mosuserver/presentation/inquiry/dto/InquiryListResponse.java new file mode 100644 index 00000000..ba42c635 --- /dev/null +++ b/src/main/java/life/mosu/mosuserver/presentation/inquiry/dto/InquiryListResponse.java @@ -0,0 +1,14 @@ +package life.mosu.mosuserver.presentation.inquiry.dto; + +public record InquiryListResponse( + InquiryResponse inquiry, + InquiryAnswerResponse reply +) { + + public static InquiryListResponse of( + InquiryResponse inquiry, + InquiryAnswerResponse reply + ) { + return new InquiryListResponse(inquiry, reply); + } +} diff --git a/src/main/java/life/mosu/mosuserver/presentation/inquiry/dto/InquiryResponse.java b/src/main/java/life/mosu/mosuserver/presentation/inquiry/dto/InquiryResponse.java index 33332506..e2a8cf04 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/inquiry/dto/InquiryResponse.java +++ b/src/main/java/life/mosu/mosuserver/presentation/inquiry/dto/InquiryResponse.java @@ -1,7 +1,6 @@ package life.mosu.mosuserver.presentation.inquiry.dto; import io.swagger.v3.oas.annotations.media.Schema; -import life.mosu.mosuserver.domain.inquiry.entity.InquiryJpaEntity; @Schema(description = "1:1 문의 응답 DTO") public record InquiryResponse( @@ -24,14 +23,5 @@ public record InquiryResponse( String createdAt ) { - public static InquiryResponse of(InquiryJpaEntity inquiry) { - return new InquiryResponse( - inquiry.getId(), - inquiry.getTitle(), - inquiry.getContent(), - inquiry.getAuthor(), - inquiry.getStatus().getStatusName(), - inquiry.getCreatedAt() - ); - } + } \ No newline at end of file