Skip to content

Commit

Permalink
feat: 경험 상품 조회 페이징 API 구현 (#265)
Browse files Browse the repository at this point in the history
* chore: 경험 선물 상세 API note 컬럼 추가

* feat: 경험선물 <-> 경험카테고리, 경험선물 <-> 상황카테고리 맵핑 엔티티 생성

* feat: 경험 선물 인기순 정렬 Paging API 구현

* feat: 경험 선물 상황/경험별 선물을, 인기/가격높은순/가격낮은순에 따라 정렬 후 Response API 구현

* chore: 코드 정리
  • Loading branch information
sejineer authored Feb 21, 2024
1 parent 9ff9f1d commit 4c25f0a
Show file tree
Hide file tree
Showing 15 changed files with 440 additions and 218 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,29 @@
import com.shallwe.domain.experiencegift.dto.response.ExperienceRes;
import com.shallwe.domain.experiencegift.dto.response.ExperienceSttCategoryRes;
import com.shallwe.global.config.security.token.UserPrincipal;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;

import java.util.List;

public interface ExperienceGiftService {

List<ExperienceRes> searchExperience(UserPrincipal userPrincipal, String title);

ExperienceDetailRes getExperienceDetails(final UserPrincipal userPrincipal, Long ExperienceGiftId);

List<ExperienceSttCategoryRes> highSttCategoryPricedGift(UserPrincipal userPrincipal, Long SttCategoryId);

List<ExperienceSttCategoryRes> lowSttCategoryPricedGift(UserPrincipal userPrincipal, Long sttCategoryId);

List<ExperienceExpCategoryRes> highExpCategoryPricedGift(UserPrincipal userPrincipal, Long expCategoryId);

List<ExperienceExpCategoryRes> lowExpCategoryPricedGift(UserPrincipal userPrincipal, Long expCategoryId);

List<ExperienceSttCategoryRes> getPopularSttGift(UserPrincipal userPrincipal, Long sttCategoryId);

List<ExperienceExpCategoryRes> getPopulaExpGift(UserPrincipal userPrincipal, Long expCategoryId);

ExperienceMainRes mainPage(UserPrincipal userPrincipal);

List<ExperienceRes> getAllPopularGift(UserPrincipal userPrincipal);

void registerExperienceGift(UserPrincipal userPrincipal, ShopOwnerExperienceReq shopOwnerExperienceReq);

ShopOwnerMainRes mainAdminExperienceGift(UserPrincipal userPrincipal);

List<ShopOwnerExperienceRes> getExperienceGift(UserPrincipal userPrincipal);

void modifyExperienceGift(Long experienceGiftId, UserPrincipal userPrincipal, ShopOwnerExperienceReq shopOwnerExperienceReq);

void deleteExperienceGift(Long experienceGiftId, UserPrincipal userPrincipal);

ShopOwnerExperienceDetailsRes getExperienceGiftDetails(UserPrincipal userPrincipal, Long experienceGiftId);
Slice<ExperienceGiftRes> getPagedExperienceGifts(Pageable pageable, String sttCategory, String searchCondition, String expCategory, String sortCondition);

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import com.shallwe.global.config.security.token.UserPrincipal;
import com.shallwe.global.utils.AwsS3ImageUrlUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
Expand Down Expand Up @@ -329,4 +331,10 @@ public List<ExperienceExpCategoryRes> getPopulaExpGift(UserPrincipal userPrincip
}).collect(Collectors.toList());
}

@Override
public Slice<ExperienceGiftRes> getPagedExperienceGifts(final Pageable pageable, final String sttCategory,
final String searchCondition, final String expCategory, final String sortCondition) {
return experienceGiftRepository.findPagedExperienceGifts(pageable, sttCategory, searchCondition, expCategory, sortCondition);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,41 @@
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "experience_gift")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Entity
public class ExperienceGift extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "title")
private String title;

@Column(name = "price")
private Long price;

@Column(name = "description")
private String description;

@Column(name = "gift_img_url")
private String giftImgUrl;

@Column(name = "location")
private String location;

@Column(name = "note")
private String note;

@Column(name = "reservation_count")
private Long reservationCount; // 인기순 정렬을 위한 WAITING이 아닌 Reservation 개수 카운트

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "subtitle_id")
private Subtitle subtitle;

private Long price;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "experience_category_id")
private ExperienceCategory experienceCategory;
Expand All @@ -35,18 +53,10 @@ public class ExperienceGift extends BaseEntity {
@JoinColumn(name = "situation_category_id")
private SituationCategory situationCategory;

private String description;

private String giftImgKey;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "shopOwner_id")
private ShopOwner shopOwner;

private String location;

private String note;

@OneToMany(mappedBy = "experienceGift")
private List<ExperienceGiftImage> imgList = new ArrayList<>();

Expand Down Expand Up @@ -74,15 +84,23 @@ public void update(ShopOwnerExperienceReq shopOwnerExperienceReq, Subtitle subti
this.note=shopOwnerExperienceReq.getNote();
}

public void addReservationCount() {
this.reservationCount++;
}

public void subtractReservationCount() {
this.reservationCount--;
}

@Builder
public ExperienceGift(String title, Subtitle subtitle, Long price, ExperienceCategory experienceCategory, SituationCategory situationCategory, String description, String giftImgKey, ShopOwner shopOwner, String location, String note, List<ExperienceGiftImage> imgList) {
public ExperienceGift(String title, Subtitle subtitle, Long price, ExperienceCategory experienceCategory, SituationCategory situationCategory, String description, String giftImgUrl, ShopOwner shopOwner, String location, String note, List<ExperienceGiftImage> imgList) {
this.title = title;
this.subtitle = subtitle;
this.price = price;
this.experienceCategory = experienceCategory;
this.situationCategory = situationCategory;
this.description = description;
this.giftImgKey = giftImgKey;
this.giftImgUrl = giftImgUrl;
this.shopOwner = shopOwner;
this.location = location;
this.note = note;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.shallwe.domain.experiencegift.domain;

import com.shallwe.domain.common.BaseEntity;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "experience_gift_experience_category")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class ExperienceGiftExperienceCategory extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(nullable = false, name = "experience_gift_id")
private ExperienceGift experienceGift;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(nullable = false, name = "experience_category_id")
private ExperienceCategory experienceCategory;

@Builder
public ExperienceGiftExperienceCategory(ExperienceGift experienceGift, ExperienceCategory experienceCategory) {
this.experienceGift = experienceGift;
this.experienceCategory = experienceCategory;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.shallwe.domain.experiencegift.domain;

import com.shallwe.domain.common.BaseEntity;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "experience_gift_situation_category")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class ExperienceGiftSituationCategory extends BaseEntity {

@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(nullable = false, name = "experience_gift_id")
private ExperienceGift experienceGift;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(nullable = false, name = "situation_category_id")
private SituationCategory situationCategory;

@Builder
public ExperienceGiftSituationCategory(ExperienceGift experienceGift, SituationCategory situationCategory) {
this.experienceGift = experienceGift;
this.situationCategory = situationCategory;
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.shallwe.domain.experiencegift.domain.repository;

import com.shallwe.domain.experiencegift.domain.ExperienceGift;
import com.shallwe.domain.experiencegift.dto.response.ExperienceGiftRes;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;

import java.util.List;

Expand All @@ -13,5 +16,5 @@ public interface ExperienceGiftQuerydslRepository {
List<ExperienceGift> findPopularGiftsBySttCategoryId(Long sttCategoryId);
List<ExperienceGift> findPopularGiftsByExpCategoryId(Long ExpCategoryId);
List<ExperienceGift> findAllPopularGifts();

Slice<ExperienceGiftRes> findPagedExperienceGifts(Pageable pageable, String sttCategory, String searchCondition, String expCategory, String sortCondition);
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
package com.shallwe.domain.experiencegift.domain.repository;

import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.shallwe.domain.common.Status;
import com.shallwe.domain.experiencegift.domain.ExperienceGift;
import com.shallwe.domain.experiencegift.domain.QExperienceCategory;
import com.shallwe.domain.experiencegift.domain.QExperienceGiftExperienceCategory;
import com.shallwe.domain.experiencegift.domain.QSituationCategory;
import com.shallwe.domain.experiencegift.dto.response.ExperienceGiftRes;
import com.shallwe.domain.experiencegift.dto.response.QExperienceGiftRes;
import com.shallwe.domain.reservation.domain.QReservation;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.SliceImpl;
import org.springframework.stereotype.Repository;

import java.util.List;

import static com.shallwe.domain.experiencegift.domain.QExperienceCategory.*;
import static com.shallwe.domain.experiencegift.domain.QExperienceGift.experienceGift;
import static com.shallwe.domain.experiencegift.domain.QExperienceGiftExperienceCategory.*;
import static com.shallwe.domain.experiencegift.domain.QExperienceGiftSituationCategory.*;
import static com.shallwe.domain.experiencegift.domain.QSituationCategory.*;
import static com.shallwe.global.config.Constant.ExperienceGiftConstant.*;


@RequiredArgsConstructor
Expand Down Expand Up @@ -92,4 +107,70 @@ public List<ExperienceGift> findAllPopularGifts() {
.fetch();
}

@Override
public Slice<ExperienceGiftRes> findPagedExperienceGifts(final Pageable pageable, final String sttCategory, final String searchCondition, final String expCategory, final String sortCondition) {
List<ExperienceGiftRes> results = queryFactory
.select(new QExperienceGiftRes(
experienceGift.id,
experienceGift.giftImgUrl,
experienceGift.subtitle.title,
experienceGift.title,
experienceGift.price
))
.distinct()
.from(experienceGift)
.leftJoin(experienceGift.subtitle)
.leftJoin(experienceGiftExperienceCategory).on(experienceGiftExperienceCategory.experienceGift.id.eq(experienceGift.id))
.leftJoin(experienceGiftSituationCategory).on(experienceGiftSituationCategory.experienceGift.id.eq(experienceGift.id))
.where(
experienceGift.status.eq(Status.ACTIVE),
sttCategoryEq(sttCategory),
expCategoryEq(expCategory),
searchConditionEq(searchCondition)
)
.orderBy(orderBy(sortCondition)) // WAITING이 아닌 예약의 개수를 기준으로 정렬
.offset(pageable.getOffset())
.limit(pageable.getPageSize() + 1) // +1 해서 다음 페이지가 있는지 체크
.fetch();

boolean hasNext = results.size() > pageable.getPageSize();
List<ExperienceGiftRes> content = hasNext ? results.subList(0, pageable.getPageSize()) : results;

return new SliceImpl<>(content, pageable, hasNext);
}

private BooleanExpression searchConditionEq(String searchCondition) {
if (searchCondition == null) {
return null;
}
return experienceGift.title.contains(searchCondition).or(experienceGift.subtitle.title.contains(searchCondition));
}

private BooleanExpression sttCategoryEq(String sttCategory) {
if (sttCategory == null) {
return null;
}
return experienceGiftSituationCategory.situationCategory.sttCategory.eq(sttCategory);
}

private BooleanExpression expCategoryEq(String expCategory) {
if (expCategory == null) {
return null;
}
return experienceGiftExperienceCategory.experienceCategory.expCategory.eq(expCategory);
}

private OrderSpecifier<?> orderBy(String condition) {
if (condition.equals(POPULAR_EXPERIENCE_GIFT)) { // 인기순
return experienceGift.reservationCount.desc().nullsLast();
}
if (condition.equals(HIGH_PRICED_ORDER)) { // 가격 높은 순
return experienceGift.price.desc();
}
if (condition.equals(LOW_PRICED_ORDER)) { // 가격 낮은 순
return experienceGift.price.asc();
}
return experienceGift.reservationCount.desc().nullsLast(); // Default 인기순
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.shallwe.domain.experiencegift.dto.response;

import com.querydsl.core.annotations.QueryProjection;
import lombok.Data;

@Data
public class ExperienceGiftRes {

private Long experienceGiftId;
private String giftImgUrl;
private String subtitle;
private String title;
private Long price;

@QueryProjection
public ExperienceGiftRes(Long experienceGiftId, String giftImgUrl, String subtitle, String title, Long price) {
this.experienceGiftId = experienceGiftId;
this.giftImgUrl = giftImgUrl;
this.subtitle = subtitle;
this.title = title;
this.price = price;
}

}
Loading

0 comments on commit 4c25f0a

Please sign in to comment.