Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MDH-109] 유저의 예약 및 웨이팅 매장 필터 조회 #113

Merged
merged 7 commits into from
Sep 20, 2023
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ owner/src/main/resources
user/src/main/resources

### ascii-doc ###
src/main/resources/static/docs/index.html
owner/src/main/resources/static/docs/owner-index.html
user/src/main/resources/static/docs/user-index.html

# End of https://www.toptal.com/developers/gitignore/api/intellij,macos,java,gradle
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
package com.mdh.common.shop.persistence;

import com.mdh.common.shop.persistence.dto.ReservationShopSearchQueryDto;
import com.mdh.common.shop.persistence.dto.ShopQueryDto;
import com.querydsl.jpa.impl.JPAQuery;
import lombok.NonNull;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.util.MultiValueMap;

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

public interface ShopRepositoryCustom {

List<ShopQueryDto> searchShopCondition(MultiValueMap<String, String> cond, Pageable pageable);

JPAQuery<Long> searchShopConditionCount(MultiValueMap<String, String> cond);

public Page<ReservationShopSearchQueryDto> searchReservationShopByFilter(Pageable pageable,
@NonNull LocalDate reservationDate,
@NonNull LocalTime reservationTime,
Integer personCount,
String regionName,
Integer minPrice,
Integer maxPrice);
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
package com.mdh.common.shop.persistence;

import com.mdh.common.reservation.domain.SeatStatus;
import com.mdh.common.shop.domain.Shop;
import com.mdh.common.shop.domain.ShopType;
import com.mdh.common.shop.persistence.dto.ReservationShopSearchQueryDto;
import com.mdh.common.shop.persistence.dto.ShopQueryDto;
import com.mdh.common.shop.persistence.dto.ShopSearchCondParam;
import com.mdh.common.waiting.domain.ShopWaitingStatus;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Order;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.*;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.stereotype.Repository;
import org.springframework.util.MultiValueMap;

import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static com.mdh.common.reservation.domain.QShopReservation.shopReservation;
import static com.mdh.common.reservation.domain.QShopReservationDateTime.shopReservationDateTime;
import static com.mdh.common.reservation.domain.QShopReservationDateTimeSeat.shopReservationDateTimeSeat;
import static com.mdh.common.shop.domain.QRegion.region;
import static com.mdh.common.shop.domain.QShop.shop;
import static com.mdh.common.shop.persistence.dto.ShopSearchCondParam.*;
Expand Down Expand Up @@ -64,6 +77,118 @@ public JPAQuery<Long> searchShopConditionCount(MultiValueMap<String, String> con
.where(shopQueryDynamicCond(cond));
}

@Override
public Page<ReservationShopSearchQueryDto> searchReservationShopByFilter(Pageable pageable,
@NonNull LocalDate reservationDate,
@NonNull LocalTime reservationTime,
Integer personCount,
String regionName,
Integer minPrice,
Integer maxPrice) {
var content = jpaQueryFactory
.select(Projections.constructor(ReservationShopSearchQueryDto.class,
shop.id,
shop.name,
shop.description,
shop.shopType,
region.city,
region.district,
shop.shopPrice,
seatStatusCaseBuilder().sum().as("availableSeatCount")
))
.from(shopReservationDateTimeSeat)
.join(shopReservationDateTime)
.on(shopReservationDateTimeSeat.shopReservationDateTime.id.eq(shopReservationDateTime.id))
.join(shopReservation)
.on(shopReservation.shopId.eq(shopReservationDateTime.shopReservation.shopId))
.join(shop)
.on(shopReservation.shopId.eq(shop.id))
.join(region)
.on(shop.region.id.eq(region.id))
.where(booleanBuilder(reservationDate, reservationTime, personCount, regionName, minPrice, maxPrice))
.groupBy(shop.id) // 아이디로 group by
.orderBy(getOrderSpecifiers(pageable))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();

var totalCount = jpaQueryFactory
.select(shop.countDistinct())
.from(shopReservationDateTimeSeat)
.join(shopReservationDateTime)
.on(shopReservationDateTimeSeat.shopReservationDateTime.id.eq(shopReservationDateTime.id))
.join(shopReservation)
.on(shopReservation.shopId.eq(shopReservationDateTime.shopReservation.shopId))
.join(shop)
.on(shopReservation.shopId.eq(shop.id))
.join(region)
.on(shop.region.id.eq(region.id))
.where(booleanBuilder(reservationDate, reservationTime, personCount, regionName, minPrice, maxPrice));

return PageableExecutionUtils.getPage(content, pageable, totalCount::fetchOne);
}

private OrderSpecifier[] getOrderSpecifiers(Pageable pageable) {
var sort = pageable.getSort();
var orderSpecifiers = new ArrayList<>();
orderSpecifiers.add(new OrderSpecifier<>(Order.DESC, Expressions.stringPath("availableSeatCount")));
var shopOrderSpecifiers = sort.get().map(o -> {
Order order = o.isAscending() ? Order.ASC : Order.DESC;
String property = o.getProperty();
PathBuilder<Shop> pathBuilder = new PathBuilder<>(Shop.class, "shop");
return new OrderSpecifier(order, pathBuilder.getString(property));
}).toList();
orderSpecifiers.addAll(shopOrderSpecifiers);
return orderSpecifiers.stream().toArray(OrderSpecifier[]::new);
}

private NumberExpression<Integer> seatStatusCaseBuilder() {
return new CaseBuilder()
.when(shopReservationDateTimeSeat.seatStatus.eq(SeatStatus.AVAILABLE))
.then(1)
.otherwise(0);
}

private BooleanBuilder booleanBuilder(LocalDate reservationDate,
LocalTime reservationTime,
Integer personCount,
String regionName,
Integer minPrice,
Integer maxPrice) {
return new BooleanBuilder().and(reservationDateTimeEq(reservationDate, reservationTime))
.and(personLoeGoe(personCount))
.and(regionContains(regionName))
.and(priceLoeGoe(minPrice, maxPrice));
}

private BooleanExpression reservationDateTimeEq(LocalDate reservationDate, LocalTime reservationTime) {
return shopReservationDateTime.reservationDate.eq(reservationDate).and(
shopReservationDateTime.reservationTime.eq(reservationTime)
);
}

private BooleanExpression personLoeGoe(Integer personCount) {
return personCount != null ? shopReservation.minimumPerson.loe(personCount)
.and(shopReservation.maximumPerson.goe(personCount)) : null;
}

private BooleanExpression regionContains(String regionName) {
return regionName != null ? region.city.contains(regionName).or(region.district.contains(regionName)) : null;
}

private BooleanExpression priceLoeGoe(Integer minPrice, Integer maxPrice) {
if (minPrice == null) return maxPriceGoe(maxPrice);
return minPriceLoe(minPrice).and(maxPriceGoe(maxPrice));
}

private BooleanExpression minPriceLoe(Integer minPrice) {
return minPrice != null ? shop.shopPrice.shopMinPrice.loe(minPrice) : null;
}

private BooleanExpression maxPriceGoe(Integer maxPrice) {
return maxPrice != null ? shop.shopPrice.shopMaxPrice.goe(maxPrice) : null;
}

private BooleanBuilder shopQueryDynamicCond(final MultiValueMap<String, String> cond) {
var booleanBuilder = new BooleanBuilder();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.mdh.common.shop.persistence.dto;

import com.mdh.common.shop.domain.ShopPrice;
import com.mdh.common.shop.domain.ShopType;

public record ReservationShopSearchQueryDto(
Long id,
String name,
String description,
ShopType shopType,
String city,
String district,
ShopPrice shopPrice,
int availableSeatCount
) {
}
16 changes: 16 additions & 0 deletions common/src/test/java/com/mdh/common/DataInitializerFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ public static Region region() {
.build();
}

public static Region region(String city, String district) {
return Region.builder()
.city(city)
.district(district)
.build();
}

public static ShopDetails shopDetails() {
return ShopDetails.builder()
.url("https://www.example.com")
Expand All @@ -65,6 +72,15 @@ public static ShopAddress shopAddress() {
.build();
}

public static ShopPrice shopPrice() {
return ShopPrice.builder()
.lunchMinPrice(10000)
.lunchMaxPrice(50000)
.dinnerMinPrice(30000)
.dinnerMaxPrice(80000)
.build();
}

public static Shop shop(
Long userId,
ShopDetails shopDetails,
Expand Down
Loading
Loading