Skip to content
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.sumte.guesthouse.controller;

import org.springdoc.core.annotations.ParameterObject;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
Expand Down Expand Up @@ -71,7 +74,37 @@ public ApiResponse<Void> updateGuesthouse(
@RequestBody @Valid GuesthouseRequestDTO.Update dto) {
guesthouseCommandService.updateGuesthouse(guesthouseId, dto);
return ApiResponse.successWithNoData();
}

// @Operation(summary = "홈 화면 게스트하우스 목록 조회 (광고 우선)", description = "게스트하우스 목록을 보여줍니다")
// @GetMapping("/home")
// public ApiResponse<Slice<GuesthouseResponseDTO.HomeSummary>> getGuesthousesForHome(
// @ParameterObject
// @PageableDefault(size = 10) Pageable pageable) {
// return ApiResponse.success(guesthouseQueryService.getGuesthousesForHome(pageable));
// }

@GetMapping("/home")
public ResponseEntity<ApiResponse<Slice<GuesthouseResponseDTO.HomeSummary>>> getGuesthousesForHome(
@ParameterObject
@PageableDefault(size = 10) Pageable pageable) {
Slice<GuesthouseResponseDTO.HomeSummary> data = guesthouseQueryService.getGuesthousesForHome(pageable);
ApiResponse<Slice<GuesthouseResponseDTO.HomeSummary>> response = ApiResponse.success(data);
return ResponseEntity.ok(response);
}

@PatchMapping("/{guesthouseId}/advertisement/on")
@Operation(summary = "게스트하우스 광고 설정", description = "해당 게스트하우스를 광고 상태로 설정합니다.")
public ApiResponse<Void> activateAdvertisement(@PathVariable Long guesthouseId) {
guesthouseCommandService.activateAdvertisement(guesthouseId);
return ApiResponse.successWithNoData();
}

@PatchMapping("/{guesthouseId}/advertisement/off")
@Operation(summary = "게스트하우스 광고 해제", description = "해당 게스트하우스를 광고 상태에서 해제합니다.")
public ApiResponse<Void> deactivateAdvertisement(@PathVariable Long guesthouseId) {
guesthouseCommandService.deactivateAdvertisement(guesthouseId);
return ApiResponse.successWithNoData();
}

@GetMapping("/{guesthouseId}")
Expand Down Expand Up @@ -102,4 +135,4 @@ public ResponseEntity<ApiResponse<Page<GuesthousePreviewDTO>>> searchGuesthouse(

}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import com.sumte.guesthouse.dto.GuesthouseRequestDTO;
import com.sumte.guesthouse.dto.GuesthouseResponseDTO;
import com.sumte.guesthouse.entity.AdType;
import com.sumte.guesthouse.entity.Guesthouse;

import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -36,4 +37,24 @@ public GuesthouseResponseDTO.Update toUpdateResponseDTO(Guesthouse guesthouse, L
.optionServices(optionServices)
.build();
}

public GuesthouseResponseDTO.HomeSummary toHomeSummary(
Guesthouse guesthouse,
Double avgScore,
int reviewCount,
String checkInTime,
Long minPrice
) {
return GuesthouseResponseDTO.HomeSummary.builder()
.guestHouseId(guesthouse.getId())
.name(guesthouse.getName())
.addressRegion(guesthouse.getAddressRegion())
.imageUrl(guesthouse.getImageUrl())
.averageScore(avgScore)
.reviewCount(reviewCount)
.checkInTime(checkInTime)
.minPrice(minPrice)
.isAd(guesthouse.getAdvertisement() == AdType.AD)
.build();
}
}
17 changes: 17 additions & 0 deletions src/main/java/com/sumte/guesthouse/dto/GuesthouseResponseDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class GuesthouseResponseDTO {
public static class Register {
Long id;
String name;
String addressRegion;
}

@Builder
Expand Down Expand Up @@ -62,4 +63,20 @@ public static class GetHouseResponse {

}

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class HomeSummary {
private Long guestHouseId;
private String name;
private String addressRegion;
private String imageUrl;
private Double averageScore;
private int reviewCount;
private String checkInTime;
private Long minPrice;
private boolean isAd;
}

}
8 changes: 8 additions & 0 deletions src/main/java/com/sumte/guesthouse/entity/Guesthouse.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,12 @@ public void setInformation(String information) {
this.information = information;
}

public void activateAd() {
this.advertisement = AdType.AD;
}

public void deactivateAd() {
this.advertisement = AdType.NON_AD;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,28 @@

import java.util.Optional;

import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import com.sumte.guesthouse.entity.Guesthouse;

public interface GuesthouseRepository extends JpaRepository<Guesthouse, Long> {
Guesthouse findByNameAndAddressDetail(String name, String addressDetails);

Optional<Guesthouse> findById(Long id);

@Query("""
SELECT g FROM Guesthouse g
LEFT JOIN Review r ON r.room.guesthouse.id = g.id
GROUP BY g.id
ORDER BY
CASE WHEN g.advertisement = 'AD' THEN 0 ELSE 1 END,
COUNT(r) DESC,
COALESCE(AVG(r.score), 0) DESC
""")

//리뷰는 Page로 했는데 Slice로 수정해도 괜찮을거 같음 (확인 -> 전체 요소수는 필요없으니)
Slice<Guesthouse> findAllOrderedForHome(Pageable pageable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,10 @@ public interface GuesthouseCommandService {
GuesthouseResponseDTO.Update updateGuesthouse(Long id, GuesthouseRequestDTO.Update dto);

GuesthouseResponseDTO.delete deleteGuesthouse(Long guesthouseId);

@Transactional
void activateAdvertisement(Long guesthouseId);

@Transactional
void deactivateAdvertisement(Long guesthouseId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,20 @@ public GuesthouseResponseDTO.delete deleteGuesthouse(Long guesthouseId) {

}

@Override
@Transactional
public void activateAdvertisement(Long guesthouseId) {
Guesthouse guesthouse = guesthouseRepository.findById(guesthouseId)
.orElseThrow(() -> new SumteException(CommonErrorCode.NOT_EXIST));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

게스트하우스 전용 예외처리를 사용하면 더 좋을 것 같습니다!

guesthouse.activateAd();
}

@Override
@Transactional
public void deactivateAdvertisement(Long guesthouseId) {
Guesthouse guesthouse = guesthouseRepository.findById(guesthouseId)
.orElseThrow(() -> new SumteException(CommonErrorCode.NOT_EXIST));
guesthouse.deactivateAd();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;

import com.sumte.guesthouse.dto.GuesthousePreviewDTO;
import com.sumte.guesthouse.dto.GuesthouseResponseDTO;
Expand All @@ -12,4 +13,5 @@ public interface GuesthouseQueryService {

Page<GuesthousePreviewDTO> getFilteredGuesthouse(GuesthouseSearchRequestDTO dto, Pageable pageable);

Slice<GuesthouseResponseDTO.HomeSummary> getGuesthousesForHome(Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package com.sumte.guesthouse.service;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.stereotype.Service;

import com.sumte.apiPayload.code.error.CommonErrorCode;
import com.sumte.apiPayload.exception.SumteException;
import com.sumte.guesthouse.converter.GuesthouseConverter;
import com.sumte.guesthouse.dto.GuesthousePreviewDTO;
import com.sumte.guesthouse.dto.GuesthouseResponseDTO;
import com.sumte.guesthouse.dto.GuesthouseSearchRequestDTO;
Expand All @@ -17,17 +20,24 @@
import com.sumte.guesthouse.repository.GuesthouseRepository;
import com.sumte.guesthouse.repository.GuesthouseRepositoryCustom;
import com.sumte.guesthouse.repository.GuesthouseTargetAudienceRepository;
import com.sumte.review.repository.ReviewRepository;
import com.sumte.room.dto.RoomResponseDTO;
import com.sumte.room.repository.RoomRepository;

import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class GuesthouseQueryServiceImpl implements GuesthouseQueryService {

private final GuesthouseRepository guesthouseRepository;
private final RoomRepository roomRepository;
private final ReviewRepository reviewRepository;
private final GuesthouseConverter guesthouseConverter;
private final GuesthouseTargetAudienceRepository guesthouseTargetAudienceRepository;
private final GuesthouseOptionServicesRepository guesthouseOptionServicesRepository;
private final GuesthouseRepositoryCustom guesthouseRepositoryCustom;

@Override
@Transactional
Expand Down Expand Up @@ -67,13 +77,23 @@ public GuesthouseResponseDTO.GetHouseResponse getHouseById(Long id) {
.build();
}

private final GuesthouseRepositoryCustom guesthouseRepositoryCustom;
@Override
@Transactional
public Slice<GuesthouseResponseDTO.HomeSummary> getGuesthousesForHome(Pageable pageable) {
Slice<Guesthouse> guesthouses = guesthouseRepository.findAllOrderedForHome(pageable);

return guesthouses.map(gh -> guesthouseConverter.toHomeSummary(
gh,
Optional.ofNullable(reviewRepository.findAverageScoreByGuesthouseId(gh.getId())).orElse(0.0),
reviewRepository.countByGuesthouseId(gh.getId()),
Optional.ofNullable(roomRepository.findEarliestCheckinByGuesthouseId(gh.getId())).orElse("00:00"),
Optional.ofNullable(roomRepository.findMinPriceByGuesthouseId(gh.getId())).orElse(0L)
));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

체크인 시간과, 가격 같은 경우 0으로 제공되는 것보단 null이나 -1같은 특별한 값으로 프론트에서 처리할 수 있도록 하는 것이 좋아 보입니다. (관련 설명을 스웨거에도 기입해주면 좋겠죠?)


@Override
@Transactional
public Page<GuesthousePreviewDTO> getFilteredGuesthouse(GuesthouseSearchRequestDTO dto, Pageable pageable) {

return guesthouseRepositoryCustom.searchFiltered(dto, pageable);
}

}
}
10 changes: 10 additions & 0 deletions src/main/java/com/sumte/review/repository/ReviewRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import com.sumte.review.entity.Review;

Expand All @@ -19,5 +21,13 @@ public interface ReviewRepository extends JpaRepository<Review, Long> {
//내가 작성한 리뷰 조회 (단순 내가 작성한 리뷰만 조회하는거라 넣었는데 다시 확인(중복) )
Page<Review> findAllByUserId(Long userId, Pageable pageable);

//홈화면
@Query("SELECT AVG(r.score) FROM Review r WHERE r.room.guesthouse.id = :guesthouseId")
Double findAverageScoreByGuesthouseId(@Param("guesthouseId") Long guesthouseId);

@Query("SELECT COUNT(r) FROM Review r WHERE r.room.guesthouse.id = :guesthouseId")
int countByGuesthouseId(@Param("guesthouseId") Long guesthouseId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

int countByRoomGuesthouseId(Long guesthouseId); 처럼 @Query 없이 메서드 쿼리로 구현 가능해 보입니다!

// int countByRoomGuesthouseId(Long guesthouseId);

boolean existsByUserIdAndRoomGuesthouseId(Long userId, Long roomGuesthouseId);
}
18 changes: 18 additions & 0 deletions src/main/java/com/sumte/room/controller/RoomController.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.sumte.room.controller;

import java.time.LocalDate;
import java.util.List;

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -8,6 +12,7 @@
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.sumte.apiPayload.ApiResponse;
Expand Down Expand Up @@ -86,4 +91,17 @@ public ResponseEntity<ApiResponse<RoomResponseDTO.GetRoomResponse>> getRoom(
return ResponseEntity.ok(ApiResponse.success(result));
}

@GetMapping("/guesthouse/{guesthouseId}/rooms")
// @Operation(summary = "특정 게스트하우스의 객실 목록 조회", description = "선택한 날짜 기준 예약 가능한 객실만 필터링하거나 전체 보여줄 수 있습니다.")
@Operation(
summary = "특정 게스트하우스의 객실 목록 조회", description = "선택한 날짜 기준으로 예약 가능한 객실만 필터링하거나 전체를 조회할 수 있습니다.\n각 방마다 예약 가능 여부가 포함되어 응답됩니다."
)
public ApiResponse<List<RoomResponseDTO.RoomSummary>> getRoomsByGuesthouse(
@PathVariable Long guesthouseId,
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate
) {
return ApiResponse.success(roomQueryService.getRoomsByGuesthouse(guesthouseId, startDate, endDate));
}

}
14 changes: 14 additions & 0 deletions src/main/java/com/sumte/room/converter/RoomConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,18 @@ public RoomResponseDTO.Updated toUpdateEntity(Room room) {
.build();
}

public RoomResponseDTO.RoomSummary toRoomSummary(Room room, boolean isReservable) {
return RoomResponseDTO.RoomSummary.builder()
.id(room.getId())
.name(room.getName())
.price(room.getPrice())
.imageUrl(room.getImageUrl())
.standardCount(room.getStandardCount())
.totalCount(room.getTotalCount())
.checkin(room.getCheckin())
.checkout(room.getCheckout())
.isReservable(isReservable)
.build();
}

}
16 changes: 16 additions & 0 deletions src/main/java/com/sumte/room/dto/RoomResponseDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,20 @@ public static class GetRoomResponse {
String imageUrl;
}

@NoArgsConstructor
@Getter
@AllArgsConstructor
@Builder
public static class RoomSummary {
private Long id;
private String name;
private Long price;
private String imageUrl;
private Long standardCount;
private Long totalCount;
private LocalTime checkin;
private LocalTime checkout;
private boolean isReservable;
}

}
Loading