From 9ff9f1d380b94adef19f2889388a5b8fe00a6f06 Mon Sep 17 00:00:00 2001 From: Sejin Park <95167215+sejineer@users.noreply.github.com> Date: Wed, 21 Feb 2024 11:31:34 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20shopOwner=20reservationList=20?= =?UTF-8?q?=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#263)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/shopowner/domain/ShopOwner.java | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/shallwe/domain/shopowner/domain/ShopOwner.java b/src/main/java/com/shallwe/domain/shopowner/domain/ShopOwner.java index 0415ee7d..dc014878 100644 --- a/src/main/java/com/shallwe/domain/shopowner/domain/ShopOwner.java +++ b/src/main/java/com/shallwe/domain/shopowner/domain/ShopOwner.java @@ -2,57 +2,51 @@ import com.shallwe.domain.common.BaseEntity; import com.shallwe.domain.reservation.domain.Reservation; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.OneToMany; +import jakarta.persistence.*; + import java.util.List; + import lombok.*; import org.hibernate.annotations.Where; @NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity @Getter -@Where(clause = "status = 'ACTIVE'") +@Table(name = "Shop_Owner") public class ShopOwner extends BaseEntity { - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(name = "name", nullable = false) private String name; + @Column(name = "phone_number", unique = true, nullable = false) private String phoneNumber; + @Column(name = "password") private String password; + @Column(name = "marketing_consent") private Boolean marketingConsent; + @Column(name = "identification") private String identification; + @Column(name = "business_registration") private String businessRegistration; + @Column(name = "bankbook") private String bankbook; - @OneToMany + @OneToMany(mappedBy = "owner", fetch = FetchType.LAZY) private List reservationList; public void changePassword(String password) { this.password = password; } - @Builder - public ShopOwner(String name, String phoneNumber, String password, Boolean marketingConsent, String identification, String businessRegistration, String bankbook, List reservationList) { - this.name = name; - this.phoneNumber = phoneNumber; - this.password = password; - this.marketingConsent = marketingConsent; - this.identification = identification; - this.businessRegistration = businessRegistration; - this.bankbook = bankbook; - this.reservationList = reservationList; - } - public void updateIdentification(String identification) { this.identification = identification; } @@ -65,4 +59,16 @@ public void updateBankbook(String bankbook) { this.bankbook = bankbook; } + @Builder + public ShopOwner(String name, String phoneNumber, String password, Boolean marketingConsent, String identification, String businessRegistration, String bankbook, List reservationList) { + this.name = name; + this.phoneNumber = phoneNumber; + this.password = password; + this.marketingConsent = marketingConsent; + this.identification = identification; + this.businessRegistration = businessRegistration; + this.bankbook = bankbook; + this.reservationList = reservationList; + } + } From 4c25f0aff6c0c14beb838554676c6ef8d4c84b3d Mon Sep 17 00:00:00 2001 From: Sejin Park <95167215+sejineer@users.noreply.github.com> Date: Wed, 21 Feb 2024 19:48:07 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=EA=B2=BD=ED=97=98=20=EC=83=81?= =?UTF-8?q?=ED=92=88=20=EC=A1=B0=ED=9A=8C=20=ED=8E=98=EC=9D=B4=EC=A7=95=20?= =?UTF-8?q?API=20=EA=B5=AC=ED=98=84=20(#265)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: 경험 선물 상세 API note 컬럼 추가 * feat: 경험선물 <-> 경험카테고리, 경험선물 <-> 상황카테고리 맵핑 엔티티 생성 * feat: 경험 선물 인기순 정렬 Paging API 구현 * feat: 경험 선물 상황/경험별 선물을, 인기/가격높은순/가격낮은순에 따라 정렬 후 Response API 구현 * chore: 코드 정리 --- .../application/ExperienceGiftService.java | 19 +- .../ExperienceGiftServiceImpl.java | 8 + .../experiencegift/domain/ExperienceGift.java | 44 +++-- .../ExperienceGiftExperienceCategory.java | 34 ++++ .../ExperienceGiftSituationCategory.java | 33 ++++ .../ExperienceGiftQuerydslRepository.java | 5 +- .../ExperienceGiftQuerydslRepositoryImpl.java | 81 +++++++++ .../dto/response/ExperienceGiftRes.java | 24 +++ .../ExperienceGiftV2Controller.java | 48 +++++ .../ReservationManipulationService.java | 16 +- .../ReservationManipulationServiceImpl.java | 5 +- .../reservation/domain/Reservation.java | 168 +++++++++--------- .../presentation/ReservationController.java | 160 ++++++++--------- .../com/shallwe/domain/user/domain/User.java | 10 +- .../com/shallwe/global/config/Constant.java | 3 - 15 files changed, 440 insertions(+), 218 deletions(-) create mode 100644 src/main/java/com/shallwe/domain/experiencegift/domain/ExperienceGiftExperienceCategory.java create mode 100644 src/main/java/com/shallwe/domain/experiencegift/domain/ExperienceGiftSituationCategory.java create mode 100644 src/main/java/com/shallwe/domain/experiencegift/dto/response/ExperienceGiftRes.java create mode 100644 src/main/java/com/shallwe/domain/experiencegift/presentation/ExperienceGiftV2Controller.java diff --git a/src/main/java/com/shallwe/domain/experiencegift/application/ExperienceGiftService.java b/src/main/java/com/shallwe/domain/experiencegift/application/ExperienceGiftService.java index 6c73490c..e6ef5e67 100644 --- a/src/main/java/com/shallwe/domain/experiencegift/application/ExperienceGiftService.java +++ b/src/main/java/com/shallwe/domain/experiencegift/application/ExperienceGiftService.java @@ -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 searchExperience(UserPrincipal userPrincipal, String title); - ExperienceDetailRes getExperienceDetails(final UserPrincipal userPrincipal, Long ExperienceGiftId); - List highSttCategoryPricedGift(UserPrincipal userPrincipal, Long SttCategoryId); - List lowSttCategoryPricedGift(UserPrincipal userPrincipal, Long sttCategoryId); - List highExpCategoryPricedGift(UserPrincipal userPrincipal, Long expCategoryId); - List lowExpCategoryPricedGift(UserPrincipal userPrincipal, Long expCategoryId); - List getPopularSttGift(UserPrincipal userPrincipal, Long sttCategoryId); - List getPopulaExpGift(UserPrincipal userPrincipal, Long expCategoryId); - ExperienceMainRes mainPage(UserPrincipal userPrincipal); - List getAllPopularGift(UserPrincipal userPrincipal); - void registerExperienceGift(UserPrincipal userPrincipal, ShopOwnerExperienceReq shopOwnerExperienceReq); - ShopOwnerMainRes mainAdminExperienceGift(UserPrincipal userPrincipal); - List getExperienceGift(UserPrincipal userPrincipal); - void modifyExperienceGift(Long experienceGiftId, UserPrincipal userPrincipal, ShopOwnerExperienceReq shopOwnerExperienceReq); - void deleteExperienceGift(Long experienceGiftId, UserPrincipal userPrincipal); - ShopOwnerExperienceDetailsRes getExperienceGiftDetails(UserPrincipal userPrincipal, Long experienceGiftId); + Slice getPagedExperienceGifts(Pageable pageable, String sttCategory, String searchCondition, String expCategory, String sortCondition); + } diff --git a/src/main/java/com/shallwe/domain/experiencegift/application/ExperienceGiftServiceImpl.java b/src/main/java/com/shallwe/domain/experiencegift/application/ExperienceGiftServiceImpl.java index 33448805..5c8178a7 100644 --- a/src/main/java/com/shallwe/domain/experiencegift/application/ExperienceGiftServiceImpl.java +++ b/src/main/java/com/shallwe/domain/experiencegift/application/ExperienceGiftServiceImpl.java @@ -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; @@ -329,4 +331,10 @@ public List getPopulaExpGift(UserPrincipal userPrincip }).collect(Collectors.toList()); } + @Override + public Slice getPagedExperienceGifts(final Pageable pageable, final String sttCategory, + final String searchCondition, final String expCategory, final String sortCondition) { + return experienceGiftRepository.findPagedExperienceGifts(pageable, sttCategory, searchCondition, expCategory, sortCondition); + } + } diff --git a/src/main/java/com/shallwe/domain/experiencegift/domain/ExperienceGift.java b/src/main/java/com/shallwe/domain/experiencegift/domain/ExperienceGift.java index 8e35f732..2624bdc3 100644 --- a/src/main/java/com/shallwe/domain/experiencegift/domain/ExperienceGift.java +++ b/src/main/java/com/shallwe/domain/experiencegift/domain/ExperienceGift.java @@ -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; @@ -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 imgList = new ArrayList<>(); @@ -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 imgList) { + public ExperienceGift(String title, Subtitle subtitle, Long price, ExperienceCategory experienceCategory, SituationCategory situationCategory, String description, String giftImgUrl, ShopOwner shopOwner, String location, String note, List 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; diff --git a/src/main/java/com/shallwe/domain/experiencegift/domain/ExperienceGiftExperienceCategory.java b/src/main/java/com/shallwe/domain/experiencegift/domain/ExperienceGiftExperienceCategory.java new file mode 100644 index 00000000..6ae26882 --- /dev/null +++ b/src/main/java/com/shallwe/domain/experiencegift/domain/ExperienceGiftExperienceCategory.java @@ -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; + } + +} diff --git a/src/main/java/com/shallwe/domain/experiencegift/domain/ExperienceGiftSituationCategory.java b/src/main/java/com/shallwe/domain/experiencegift/domain/ExperienceGiftSituationCategory.java new file mode 100644 index 00000000..418b23ed --- /dev/null +++ b/src/main/java/com/shallwe/domain/experiencegift/domain/ExperienceGiftSituationCategory.java @@ -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; + } + +} diff --git a/src/main/java/com/shallwe/domain/experiencegift/domain/repository/ExperienceGiftQuerydslRepository.java b/src/main/java/com/shallwe/domain/experiencegift/domain/repository/ExperienceGiftQuerydslRepository.java index aacadd97..716ed392 100644 --- a/src/main/java/com/shallwe/domain/experiencegift/domain/repository/ExperienceGiftQuerydslRepository.java +++ b/src/main/java/com/shallwe/domain/experiencegift/domain/repository/ExperienceGiftQuerydslRepository.java @@ -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; @@ -13,5 +16,5 @@ public interface ExperienceGiftQuerydslRepository { List findPopularGiftsBySttCategoryId(Long sttCategoryId); List findPopularGiftsByExpCategoryId(Long ExpCategoryId); List findAllPopularGifts(); - + Slice findPagedExperienceGifts(Pageable pageable, String sttCategory, String searchCondition, String expCategory, String sortCondition); } diff --git a/src/main/java/com/shallwe/domain/experiencegift/domain/repository/ExperienceGiftQuerydslRepositoryImpl.java b/src/main/java/com/shallwe/domain/experiencegift/domain/repository/ExperienceGiftQuerydslRepositoryImpl.java index 6412dace..e8180bf2 100644 --- a/src/main/java/com/shallwe/domain/experiencegift/domain/repository/ExperienceGiftQuerydslRepositoryImpl.java +++ b/src/main/java/com/shallwe/domain/experiencegift/domain/repository/ExperienceGiftQuerydslRepositoryImpl.java @@ -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 @@ -92,4 +107,70 @@ public List findAllPopularGifts() { .fetch(); } + @Override + public Slice findPagedExperienceGifts(final Pageable pageable, final String sttCategory, final String searchCondition, final String expCategory, final String sortCondition) { + List 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 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 인기순 + } + } \ No newline at end of file diff --git a/src/main/java/com/shallwe/domain/experiencegift/dto/response/ExperienceGiftRes.java b/src/main/java/com/shallwe/domain/experiencegift/dto/response/ExperienceGiftRes.java new file mode 100644 index 00000000..1078f0ea --- /dev/null +++ b/src/main/java/com/shallwe/domain/experiencegift/dto/response/ExperienceGiftRes.java @@ -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; + } + +} diff --git a/src/main/java/com/shallwe/domain/experiencegift/presentation/ExperienceGiftV2Controller.java b/src/main/java/com/shallwe/domain/experiencegift/presentation/ExperienceGiftV2Controller.java new file mode 100644 index 00000000..0d7b67fb --- /dev/null +++ b/src/main/java/com/shallwe/domain/experiencegift/presentation/ExperienceGiftV2Controller.java @@ -0,0 +1,48 @@ +package com.shallwe.domain.experiencegift.presentation; + +import com.shallwe.domain.experiencegift.application.ExperienceGiftServiceImpl; +import com.shallwe.domain.experiencegift.dto.response.ExperienceGiftRes; +import com.shallwe.global.payload.ErrorResponse; +import com.shallwe.global.payload.ResponseCustom; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + + +@Tag(name = "ExperienceGifts V2", description = "ExperienceGifts V2 API") +@RequestMapping("/api/v2/experience-gifts") +@RestController +@RequiredArgsConstructor +public class ExperienceGiftV2Controller { + + private final ExperienceGiftServiceImpl experienceGiftService; + + @Operation(summary = "조건별 경험선물 조회(Paging)", description = "조건별 경험선물 조회합니다.(Paging)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "조건별 전체 경험선물 조회 성공", content = {@Content(mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = ExperienceGiftRes.class)))}), + @ApiResponse(responseCode = "400", description = "조건별 전체 경험선물 조회 실패", content = {@Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class))}), + }) + @GetMapping("/list") + public ResponseCustom> getPagedExperienceGiftByPopular( + @Parameter(name = "sttCategory", description = "상황 카테고리") @RequestParam(name = "sttCategory", required = false) final String sttCategory, + @Parameter(name = "expCategory", description = "경험 카테고리") @RequestParam(name = "expCategory", required = false) final String expCategory, + @Parameter(name = "searchCondition", description = "검색어") @RequestParam(name = "searchCondition", required = false) final String searchCondition, + @Parameter(name = "sortCondition", description = "인기순, 추천순, 가격높은순, 가격낮은순") @RequestParam(name = "sortCondition", required = false, defaultValue = "popular") final String sortCondition, + @Parameter(name = "pagingCondition", description = "조회 할 페이지와 페이지 크기를 입력해주세요(sort는 무시해도 됩니다. + Page는 0번부터 시작)") Pageable pageable + ) { + return ResponseCustom.OK(experienceGiftService.getPagedExperienceGifts(pageable, sttCategory, searchCondition, expCategory, sortCondition)); + } + +} diff --git a/src/main/java/com/shallwe/domain/reservation/application/ReservationManipulationService.java b/src/main/java/com/shallwe/domain/reservation/application/ReservationManipulationService.java index aa037047..a950758b 100644 --- a/src/main/java/com/shallwe/domain/reservation/application/ReservationManipulationService.java +++ b/src/main/java/com/shallwe/domain/reservation/application/ReservationManipulationService.java @@ -6,20 +6,14 @@ import com.shallwe.domain.reservation.dto.request.UserReservationCreate; import com.shallwe.domain.reservation.dto.request.UpdateReservationReq; import com.shallwe.global.config.security.token.UserPrincipal; + import java.util.List; public interface ReservationManipulationService { - public List addOwnerReservation( - OwnerReservationCreate ownerReservationCreate, - UserPrincipal userPrincipal); - - public ReservationResponse addUserReservation(UserReservationCreate reservationRequest, - UserPrincipal userPrincipal); - - public ReservationResponse updateReservation(UpdateReservationReq updateReq, - UserPrincipal userPrincipal); - - public DeleteReservationRes deleteReservation(Long id); + List addOwnerReservation(OwnerReservationCreate ownerReservationCreate, UserPrincipal userPrincipal); + ReservationResponse addUserReservation(UserReservationCreate reservationRequest, UserPrincipal userPrincipal); + ReservationResponse updateReservation(UpdateReservationReq updateReq, UserPrincipal userPrincipal); + DeleteReservationRes deleteReservation(Long id); } diff --git a/src/main/java/com/shallwe/domain/reservation/application/ReservationManipulationServiceImpl.java b/src/main/java/com/shallwe/domain/reservation/application/ReservationManipulationServiceImpl.java index 09616d77..58ac5af1 100644 --- a/src/main/java/com/shallwe/domain/reservation/application/ReservationManipulationServiceImpl.java +++ b/src/main/java/com/shallwe/domain/reservation/application/ReservationManipulationServiceImpl.java @@ -41,9 +41,7 @@ public class ReservationManipulationServiceImpl implements ReservationManipulati private final UserRepository userRepository; @Transactional - public List addOwnerReservation( - OwnerReservationCreate ownerReservationCreate, - UserPrincipal userPrincipal) { + public List addOwnerReservation(OwnerReservationCreate ownerReservationCreate, UserPrincipal userPrincipal) { ExperienceGift experienceGift = experienceGiftRepository.findById(ownerReservationCreate.getExperienceGiftId()) .orElseThrow(ExperienceGiftNotFoundException::new); @@ -75,6 +73,7 @@ public ReservationResponse addUserReservation(UserReservationCreate reservationR if (reservation.getReservationStatus().equals(WAITING)) { reservation.updateStatus(ReservationStatus.BOOKED); reservation.updateUserReservationRequest(reservationRequest, sender, receiver); + experienceGift.addReservationCount(); } else { throw new InvalidReservationException(); } diff --git a/src/main/java/com/shallwe/domain/reservation/domain/Reservation.java b/src/main/java/com/shallwe/domain/reservation/domain/Reservation.java index 68495cb5..caaeb53f 100644 --- a/src/main/java/com/shallwe/domain/reservation/domain/Reservation.java +++ b/src/main/java/com/shallwe/domain/reservation/domain/Reservation.java @@ -10,8 +10,10 @@ import com.shallwe.domain.user.domain.User; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.persistence.*; + import java.time.LocalDate; import java.time.LocalTime; + import lombok.*; import org.hibernate.annotations.Where; @@ -19,94 +21,90 @@ import java.util.List; import java.util.Optional; -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor @Entity -@Getter @Table(name = "Reservation") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter @Where(clause = "status = 'ACTIVE'") public class Reservation extends BaseEntity { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "experience_gift_id", nullable = false) - @Schema(description = "선물 ID") - private ExperienceGift experienceGift; - - @ManyToOne - @JoinColumn(name = "owner_id", nullable = false) - @Schema(description = "사장ID") - private ShopOwner owner; - - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "sender_id") - @Schema(description = "보내는이 ID") - private User sender; - - @Column(name = "persons") - private Long persons; - - @Schema(description = "예약 날짜, YYYY-DD-MM ") - @Column(name = "date") - private LocalDate date; - - @Schema(description = "예약 시간, HH:MM") - @Column(name = "time") - private LocalTime time; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "receiver_id") - private User receiver; - - @Column(name = "phone_number") - private String phoneNumber; - - @Column(name = "invitation_img") - private String invitationImg; - - @Column(name = "invitation_comment") - private String invitationComment; - - @Schema(description = "예약 상태", allowableValues = {"CANCELED", "WAITING", "BOOKED", "CONFIRMED", - "COMPLETED", "USING"}) - @Column(name = "reservation_status", nullable = false) - @Enumerated(EnumType.STRING) - private ReservationStatus reservationStatus; - - @OneToMany(mappedBy = "reservation",fetch = FetchType.LAZY) - private List memoryPhotos = new ArrayList<>(); - - @Builder - public Reservation(ExperienceGift experienceGift, ShopOwner owner, LocalDate date, - LocalTime time, - ReservationStatus reservationStatus) { - this.experienceGift = experienceGift; - this.owner = owner; - this.date = date; - this.time = time; - this.reservationStatus = reservationStatus; - } - - public void updateReservation(UpdateReservationReq updateReq) { - this.date = Optional.ofNullable(updateReq.getDate()).orElse(this.date); - this.time = Optional.ofNullable(updateReq.getTime()).orElse(this.time); - } - - public void updateUserReservationRequest(UserReservationCreate reservationRequest, User sender, - User receiver) { - this.sender = sender; - this.receiver = receiver; - this.phoneNumber = reservationRequest.getPhoneNumber(); - this.invitationComment = reservationRequest.getInvitationComment(); - this.persons = reservationRequest.getPersons(); - this.invitationImg = reservationRequest.getImageURL(); - } - - public void updateStatus(ReservationStatus status) { - this.reservationStatus = status; - } + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "experience_gift_id", nullable = false) + @Schema(description = "선물 ID") + private ExperienceGift experienceGift; + + @ManyToOne + @JoinColumn(name = "owner_id", nullable = false) + @Schema(description = "사장ID") + private ShopOwner owner; + + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "sender_id") + @Schema(description = "보내는이 ID") + private User sender; + + @Column(name = "persons") + private Long persons; + + @Schema(description = "예약 날짜, YYYY-DD-MM ") + @Column(name = "date") + private LocalDate date; + + @Schema(description = "예약 시간, HH:MM") + @Column(name = "time") + private LocalTime time; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "receiver_id") + private User receiver; + + @Column(name = "phone_number") + private String phoneNumber; + + @Column(name = "invitation_img") + private String invitationImg; + + @Column(name = "invitation_comment") + private String invitationComment; + + @Schema(description = "예약 상태", allowableValues = {"CANCELED", "WAITING", "BOOKED", "CONFIRMED", + "COMPLETED", "USING"}) + @Column(name = "reservation_status", nullable = false) + @Enumerated(EnumType.STRING) + private ReservationStatus reservationStatus; + + @OneToMany(mappedBy = "reservation", fetch = FetchType.LAZY) + private List memoryPhotos = new ArrayList<>(); + + @Builder + public Reservation(ExperienceGift experienceGift, ShopOwner owner, LocalDate date, LocalTime time, ReservationStatus reservationStatus) { + this.experienceGift = experienceGift; + this.owner = owner; + this.date = date; + this.time = time; + this.reservationStatus = reservationStatus; + } + + public void updateReservation(UpdateReservationReq updateReq) { + this.date = Optional.ofNullable(updateReq.getDate()).orElse(this.date); + this.time = Optional.ofNullable(updateReq.getTime()).orElse(this.time); + } + + public void updateUserReservationRequest(UserReservationCreate reservationRequest, User sender, User receiver) { + this.sender = sender; + this.receiver = receiver; + this.phoneNumber = reservationRequest.getPhoneNumber(); + this.invitationComment = reservationRequest.getInvitationComment(); + this.persons = reservationRequest.getPersons(); + this.invitationImg = reservationRequest.getImageURL(); + } + + public void updateStatus(ReservationStatus status) { + this.reservationStatus = status; + } } diff --git a/src/main/java/com/shallwe/domain/reservation/presentation/ReservationController.java b/src/main/java/com/shallwe/domain/reservation/presentation/ReservationController.java index db378661..b7d099f5 100644 --- a/src/main/java/com/shallwe/domain/reservation/presentation/ReservationController.java +++ b/src/main/java/com/shallwe/domain/reservation/presentation/ReservationController.java @@ -34,8 +34,8 @@ @RequestMapping("/api/v1/reservations") public class ReservationController { - private final ReservationCheckService reservationCheckService; - private final ReservationManipulationService reservationManipulationService; + private final ReservationCheckService reservationCheckService; + private final ReservationManipulationService reservationManipulationService; @Operation(summary = "예약 정보 불러오기 ", description = "저장된 모든 예약 정보") @ApiResponses(value = { @@ -44,11 +44,11 @@ public class ReservationController { @ApiResponse(responseCode = "400", description = "예약 정보 조회 실패", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class))}), - }) - @GetMapping - public ResponseCustom> getAllReservations() { - return ResponseCustom.OK(reservationCheckService.getAllReservation()); - } + }) + @GetMapping + public ResponseCustom> getAllReservations() { + return ResponseCustom.OK(reservationCheckService.getAllReservation()); + } @Operation(summary = "예약 정보 불러오기", description = "유저 ID로 검색") @ApiResponses(value = { @@ -57,13 +57,13 @@ public ResponseCustom> getAllReservations() { @ApiResponse(responseCode = "400", description = "해당 유저 예약 정보 조회 실패", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class))}), - }) - @GetMapping("/user") - public ResponseCustom> getUserReservations( - @Parameter(description = "AccessToken 을 입력해주세요.", required = true) @CurrentUser UserPrincipal userPrincipal - ) { - return ResponseCustom.OK(reservationCheckService.findUserReservation(userPrincipal)); - } + }) + @GetMapping("/user") + public ResponseCustom> getUserReservations( + @Parameter(description = "AccessToken 을 입력해주세요.", required = true) @CurrentUser UserPrincipal userPrincipal + ) { + return ResponseCustom.OK(reservationCheckService.findUserReservation(userPrincipal)); + } @Operation(summary = "가능한 예약 불러오기", description = "상품 ID로 검색") @ApiResponses(value = { @@ -73,14 +73,14 @@ public ResponseCustom> getUserReservations( @ApiResponse(responseCode = "400", description = "해당 유저 예약 정보 조회 실패", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class))}), - }) - @GetMapping("/validTimes") - public ResponseCustom> getValidReservations( - @Parameter(description = "AccessToken 을 입력해주세요.", required = true) @CurrentUser UserPrincipal userPrincipal, - @Parameter(description = "ExperienceGift ID를 입력해주세요.", required = true) @RequestParam Long giftId - ) { - return ResponseCustom.OK(reservationCheckService.getValidReservationTime(userPrincipal, giftId)); - } + }) + @GetMapping("/validTimes") + public ResponseCustom> getValidReservations( + @Parameter(description = "AccessToken 을 입력해주세요.", required = true) @CurrentUser UserPrincipal userPrincipal, + @Parameter(description = "ExperienceGift ID를 입력해주세요.", required = true) @RequestParam Long giftId + ) { + return ResponseCustom.OK(reservationCheckService.getValidReservationTime(userPrincipal, giftId)); + } @Operation(summary = "날짜로 예약 조회", description = "등록한 상품의 ID와 날짜로 이용 가능한 예약을 조회 합니다.") @ApiResponses(value = { @@ -92,13 +92,13 @@ public ResponseCustom> getValidReservations( @GetMapping("/date") public ResponseCustom> getReservationWithDate( - @Parameter(description = "AccessToken 을 입력해주세요.", required = true) @CurrentUser UserPrincipal userPrincipal, - @Parameter(description = "상품 ID를 입력해주세요", required = true) @RequestParam Long giftId, - @Parameter(description = "조회하려는 날짜를 입력해주세요 YYYY-MM-DD ", required = true) @RequestParam LocalDate date - ) { - return ResponseCustom.OK( - reservationCheckService.getReservationByDateUser(userPrincipal, giftId, date)); - } + @Parameter(description = "AccessToken 을 입력해주세요.", required = true) @CurrentUser UserPrincipal userPrincipal, + @Parameter(description = "상품 ID를 입력해주세요", required = true) @RequestParam Long giftId, + @Parameter(description = "조회하려는 날짜를 입력해주세요 YYYY-MM-DD ", required = true) @RequestParam LocalDate date + ) { + return ResponseCustom.OK( + reservationCheckService.getReservationByDateUser(userPrincipal, giftId, date)); + } @Operation(summary = "해당 경험 선물에 생성된 예약 조회 ", description = "경험 ID로 검색") @ApiResponses(value = { @@ -107,59 +107,59 @@ public ResponseCustom> getReservationWithDate( @ApiResponse(responseCode = "400", description = "예약 정보 조회 실패", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class))}), - }) - @GetMapping("/giftId") - public ResponseCustom> getCurrentGiftReservation( - @Parameter(description = "AccessToken 을 입력해주세요.", required = true) @RequestHeader Long giftId - ) { - return ResponseCustom.OK(reservationCheckService.getCurrentGiftReservation(giftId)); - } - - @Operation(summary = "유저 예약 추가하기", description = "등록된 예약이 예약 가능한지 확인 후, 예약을 확정 상태로 변경합니다.") - @ApiResponses(value = { - @ApiResponse(responseCode = "201", description = "예약 생성 성공", content = { - @Content(mediaType = "application/json", schema = @Schema(implementation = ReservationResponse.class))}), - @ApiResponse(responseCode = "400", description = "예약 생성 실패", content = { - @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class))}) - }) - @PostMapping("/user") - public ResponseCustom createUserReservation( - @Parameter(description = "예약 요청을 확인해주세요.", required = true) @RequestBody UserReservationCreate reservationRequest, - @Parameter(description = "AccessToken 을 입력해주세요.", required = true) @CurrentUser UserPrincipal userPrincipal - ) { - return ResponseCustom.CREATED( - reservationManipulationService.addUserReservation(reservationRequest, userPrincipal)); - } - - @Operation(summary = "예약 수정하기", description = "예약을 수정합니다") - @ApiResponses(value = { - @ApiResponse(responseCode = "201", description = "예약 수정 성공", content = { - @Content(mediaType = "application/json", schema = @Schema(implementation = ReservationResponse.class))}), - @ApiResponse(responseCode = "400", description = "예약 수정 실패", content = { - @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class))}) - }) - @PutMapping - public ResponseCustom updateReservation( - @Parameter(description = "수정 요청을 확인해주세요.", required = true) @RequestBody UpdateReservationReq updateReq, - @Parameter(description = "AccessToken 을 입력해주세요.", required = true) @CurrentUser UserPrincipal userPrincipal - ) { - return ResponseCustom.OK( - reservationManipulationService.updateReservation(updateReq, userPrincipal)); + }) + @GetMapping("/giftId") + public ResponseCustom> getCurrentGiftReservation( + @Parameter(description = "AccessToken 을 입력해주세요.", required = true) @RequestHeader Long giftId + ) { + return ResponseCustom.OK(reservationCheckService.getCurrentGiftReservation(giftId)); + } + @Operation(summary = "유저 예약 추가하기", description = "등록된 예약이 예약 가능한지 확인 후, 예약을 확정 상태로 변경합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "예약 생성 성공", content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = ReservationResponse.class))}), + @ApiResponse(responseCode = "400", description = "예약 생성 실패", content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class))}) + }) + @PostMapping("/user") + public ResponseCustom createUserReservation( + @Parameter(description = "예약 요청을 확인해주세요.", required = true) @RequestBody UserReservationCreate reservationRequest, + @Parameter(description = "AccessToken 을 입력해주세요.", required = true) @CurrentUser UserPrincipal userPrincipal + ) { + return ResponseCustom.CREATED( + reservationManipulationService.addUserReservation(reservationRequest, userPrincipal)); } - @Operation(summary = "예약 삭제하기", description = "예약을 삭제합니다") - @ApiResponses(value = { - @ApiResponse(responseCode = "201", description = "예약 삭제 성공", content = { - @Content(mediaType = "application/json", schema = @Schema(implementation = DeleteReservationRes.class))}), - @ApiResponse(responseCode = "400", description = "예약 삭제 실패", content = { - @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class))}) - }) - @DeleteMapping - public ResponseCustom deleteReservation( - @Parameter(description = "예약 ID를 확인해주세요.", required = true) @RequestHeader Long id - ) { - return ResponseCustom.OK(reservationManipulationService.deleteReservation(id)); - } + @Operation(summary = "예약 수정하기", description = "예약을 수정합니다") + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "예약 수정 성공", content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = ReservationResponse.class))}), + @ApiResponse(responseCode = "400", description = "예약 수정 실패", content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class))}) + }) + @PutMapping + public ResponseCustom updateReservation( + @Parameter(description = "수정 요청을 확인해주세요.", required = true) @RequestBody UpdateReservationReq updateReq, + @Parameter(description = "AccessToken 을 입력해주세요.", required = true) @CurrentUser UserPrincipal userPrincipal + ) { + return ResponseCustom.OK( + reservationManipulationService.updateReservation(updateReq, userPrincipal)); + + } + + @Operation(summary = "예약 삭제하기", description = "예약을 삭제합니다") + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "예약 삭제 성공", content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = DeleteReservationRes.class))}), + @ApiResponse(responseCode = "400", description = "예약 삭제 실패", content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class))}) + }) + @DeleteMapping + public ResponseCustom deleteReservation( + @Parameter(description = "예약 ID를 확인해주세요.", required = true) @RequestHeader Long id + ) { + return ResponseCustom.OK(reservationManipulationService.deleteReservation(id)); + } } diff --git a/src/main/java/com/shallwe/domain/user/domain/User.java b/src/main/java/com/shallwe/domain/user/domain/User.java index afd720d9..2cc38808 100644 --- a/src/main/java/com/shallwe/domain/user/domain/User.java +++ b/src/main/java/com/shallwe/domain/user/domain/User.java @@ -1,11 +1,6 @@ package com.shallwe.domain.user.domain; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; +import jakarta.persistence.*; import jakarta.validation.constraints.Email; import com.shallwe.domain.common.BaseEntity; @@ -14,8 +9,9 @@ import org.hibernate.annotations.Where; -@NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity +@Table(name = "user") +@NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter public class User extends BaseEntity { diff --git a/src/main/java/com/shallwe/global/config/Constant.java b/src/main/java/com/shallwe/global/config/Constant.java index de598937..74324547 100644 --- a/src/main/java/com/shallwe/global/config/Constant.java +++ b/src/main/java/com/shallwe/global/config/Constant.java @@ -6,8 +6,5 @@ public static class ExperienceGiftConstant{ public static final String RECOMMEND_EXPERIENCE_GIFT="추천순"; public static final String HIGH_PRICED_ORDER="가격높은순"; public static final String LOW_PRICED_ORDER="가격낮은순"; - - - } }