From 32ab0e70ae4494fe10cc10405a3fbb1b053e4c63 Mon Sep 17 00:00:00 2001 From: Sejeong Kim Date: Sun, 9 Nov 2025 22:08:03 +0900 Subject: [PATCH 01/13] =?UTF-8?q?feat:=20build.gradle=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4_umc/build.gradle | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/chapter4_umc/build.gradle b/chapter4_umc/build.gradle index 32d36f0..f7e4171 100644 --- a/chapter4_umc/build.gradle +++ b/chapter4_umc/build.gradle @@ -32,8 +32,30 @@ dependencies { annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + // QueryDSL : OpenFeign + implementation "io.github.openfeign.querydsl:querydsl-jpa:7.0" + implementation "io.github.openfeign.querydsl:querydsl-core:7.0" + annotationProcessor "io.github.openfeign.querydsl:querydsl-apt:7.0:jpa" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" } tasks.named('test') { useJUnitPlatform() } + +// QueryDSL 관련 설정 +def querydslDir = layout.buildDirectory.dir("generated/querydsl").get().asFile + +sourceSets { + main.java.srcDirs += [ querydslDir ] +} + +tasks.withType(JavaCompile).configureEach { + options.generatedSourceOutputDirectory.set(querydslDir) +} + +clean.doLast { + file(querydslDir).deleteDir() +} From cf86ea751fc6d4c433ff5d5161f7c33ad2904e8c Mon Sep 17 00:00:00 2001 From: Sejeong Kim Date: Sun, 9 Nov 2025 22:08:19 +0900 Subject: [PATCH 02/13] =?UTF-8?q?fix:=20global=20=ED=8F=B4=EB=8D=94=20?= =?UTF-8?q?=EC=95=88=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chapter4_umc/{ => global}/Chapter4UmcApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename chapter4_umc/src/main/java/com/example/chapter4_umc/{ => global}/Chapter4UmcApplication.java (88%) diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/Chapter4UmcApplication.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/global/Chapter4UmcApplication.java similarity index 88% rename from chapter4_umc/src/main/java/com/example/chapter4_umc/Chapter4UmcApplication.java rename to chapter4_umc/src/main/java/com/example/chapter4_umc/global/Chapter4UmcApplication.java index 018abdc..eb75e9c 100644 --- a/chapter4_umc/src/main/java/com/example/chapter4_umc/Chapter4UmcApplication.java +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/global/Chapter4UmcApplication.java @@ -1,4 +1,4 @@ -package com.example.chapter4_umc; +package com.example.chapter4_umc.global; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; From 5abfeab6b78fdb77ef28d84ddf5902ca00eaa391 Mon Sep 17 00:00:00 2001 From: Sejeong Kim Date: Sun, 9 Nov 2025 22:08:43 +0900 Subject: [PATCH 03/13] =?UTF-8?q?fix:=20import=20=EB=88=84=EB=9D=BD=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/chapter4_umc/domain/member/entity/Member.java | 3 +++ .../example/chapter4_umc/domain/mission/entity/Mission.java | 1 + .../com/example/chapter4_umc/domain/store/entity/Store.java | 2 ++ 3 files changed, 6 insertions(+) diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/member/entity/Member.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/member/entity/Member.java index 532ed5b..358c16e 100644 --- a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/member/entity/Member.java +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/member/entity/Member.java @@ -1,6 +1,9 @@ // Member 클래스가 속한 패키지 경로 package com.example.chapter4_umc.domain.member.entity; +import com.example.chapter4_umc.domain.notification.entity.Notification; +import com.example.chapter4_umc.domain.review.entity.Review; +import com.example.chapter4_umc.domain.inquiry.entity.Inquiry; import com.example.chapter4_umc.domain.region.entity.Region; import com.example.chapter4_umc.domain.member.enums.Gender; import org.hibernate.annotations.ColumnDefault; diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/entity/Mission.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/entity/Mission.java index c5fadfa..ed3330b 100644 --- a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/entity/Mission.java +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/entity/Mission.java @@ -1,5 +1,6 @@ package com.example.chapter4_umc.domain.mission.entity; +import com.example.chapter4_umc.domain.member.entity.MemberMission; import com.example.chapter4_umc.domain.store.entity.Store; import jakarta.persistence.*; import lombok.*; diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/store/entity/Store.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/store/entity/Store.java index 08b84ac..343a598 100644 --- a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/store/entity/Store.java +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/store/entity/Store.java @@ -1,5 +1,7 @@ package com.example.chapter4_umc.domain.store.entity; +import com.example.chapter4_umc.domain.mission.entity.Mission; +import com.example.chapter4_umc.domain.review.entity.Review; import com.example.chapter4_umc.domain.region.entity.Region; import jakarta.persistence.*; import lombok.*; From 42b14f192be302009ed08a361ad1bc0ca04bdab2 Mon Sep 17 00:00:00 2001 From: Sejeong Kim Date: Sun, 9 Nov 2025 22:09:16 +0900 Subject: [PATCH 04/13] =?UTF-8?q?fix:=20=EB=A6=AC=EB=B7=B0=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EC=A1=B0=EA=B1=B4=20DTO=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../review/dto/ReviewSearchCondition.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/ReviewSearchCondition.java diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/ReviewSearchCondition.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/ReviewSearchCondition.java new file mode 100644 index 0000000..24bbf71 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/ReviewSearchCondition.java @@ -0,0 +1,19 @@ +package com.example.chapter4_umc.domain.review.dto; + +public class ReviewSearchCondition { + private String storeName; + private String ratingRange; + + public String getStoreName() { + return storeName; + } + public void setStoreName(String storeName) { + this.storeName = storeName; + } + public String getRatingRange() { + return ratingRange; + } + public void setRatingRange(String ratingRange) { + this.ratingRange = ratingRange; + } +} From b195b30fd0bedb02084788dbf84590fa643b8e4d Mon Sep 17 00:00:00 2001 From: Sejeong Kim Date: Sun, 9 Nov 2025 22:09:43 +0900 Subject: [PATCH 05/13] =?UTF-8?q?feat:=20=EB=A6=AC=EB=B7=B0=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=EB=A5=BC=20=EC=9C=84=ED=95=9C=20DTO=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/review/dto/ReviewDto.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/ReviewDto.java diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/ReviewDto.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/ReviewDto.java new file mode 100644 index 0000000..8a22af6 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/ReviewDto.java @@ -0,0 +1,34 @@ +package com.example.chapter4_umc.domain.review.dto; + +public class ReviewDto { + private Long reviewId; + private Integer rating; + private String content; + private String storeName; + private String imageUrl; + + public ReviewDto(Long reviewId, Integer rating, String content, String storeName, String imageUrl) { + this.reviewId = reviewId; + this.rating = rating; + this.content = content; + this.storeName = storeName; + this.imageUrl = imageUrl; + } + + public Long getReviewId() { + return reviewId; + } + public Integer getRating() { + return rating; + } + public String getContent() { + return content; + } + public String getStoreName() { + return storeName; + } + public String getImageUrl() { + return imageUrl; + } + +} From 9a0c626e128d71ec5d42b9e055e97ea40af7daea Mon Sep 17 00:00:00 2001 From: Sejeong Kim Date: Sun, 9 Nov 2025 22:10:15 +0900 Subject: [PATCH 06/13] =?UTF-8?q?feat:=20=EB=A6=AC=EB=B7=B0=20=EC=BB=A4?= =?UTF-8?q?=EC=8A=A4=ED=85=80=20=EB=A0=88=ED=8F=AC=EC=A7=80=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../review/repository/ReviewRepositoryCustom.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/repository/ReviewRepositoryCustom.java diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/repository/ReviewRepositoryCustom.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/repository/ReviewRepositoryCustom.java new file mode 100644 index 0000000..385134c --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/repository/ReviewRepositoryCustom.java @@ -0,0 +1,11 @@ +package com.example.chapter4_umc.domain.review.repository; + +import com.example.chapter4_umc.domain.review.dto.ReviewDto; +import com.example.chapter4_umc.domain.review.dto.ReviewSearchCondition; +import com.example.chapter4_umc.domain.review.entity.Review; + +import java.util.List; +public interface ReviewRepositoryCustom { + List findMyReviewsWithFilter(Long memberId, ReviewSearchCondition condition); +} + From ef8965259e13618a9326abe2f995ab23e9675b0f Mon Sep 17 00:00:00 2001 From: Sejeong Kim Date: Sun, 9 Nov 2025 22:11:00 +0900 Subject: [PATCH 07/13] =?UTF-8?q?feat:=20=EB=A6=AC=EB=B7=B0=20=EC=BB=A4?= =?UTF-8?q?=EC=8A=A4=ED=85=80=20=EB=A0=88=ED=8F=AC=EC=A7=80=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EA=B5=AC=ED=98=84=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?ID,=20=EA=B0=80=EA=B2=8C=EB=AA=85,=20=EB=B3=84=EC=A0=90=20?= =?UTF-8?q?=ED=95=84=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReviewRepositoryCustomlmpI.java | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/repository/ReviewRepositoryCustomlmpI.java diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/repository/ReviewRepositoryCustomlmpI.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/repository/ReviewRepositoryCustomlmpI.java new file mode 100644 index 0000000..13a2051 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/repository/ReviewRepositoryCustomlmpI.java @@ -0,0 +1,68 @@ +package com.example.chapter4_umc.domain.review.repository; + +import com.example.chapter4_umc.domain.review.dto.ReviewDto; +import com.example.chapter4_umc.domain.review.dto.ReviewSearchCondition; +import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +import static com.example.chapter4_umc.domain.review.entity.QReview.review; +import static com.example.chapter4_umc.domain.store.entity.QStore.store; + +@Repository +@RequiredArgsConstructor +public class ReviewRepositoryCustomlmpI implements ReviewRepositoryCustom { + private final JPAQueryFactory queryFactory; + + @Override + public List findMyReviewsWithFilter(Long memberId, ReviewSearchCondition condition) { + return queryFactory + .select(Projections.constructor( + ReviewDto.class, + review.id, + review.rating, + review.content, + review.store.storeName, + review.imageUrl + )) + .from(review) + .join(review.store, store) + .where( + memberIdEq(memberId), + storeNameEq(condition.getStoreName()), + ratingEq(condition.getRatingRange()) + ) + .orderBy(review.createdAt.desc()) + .fetch(); + } + // 사용자 ID 필터링 + private BooleanExpression memberIdEq(Long memberId) { + return review.member.id.eq(memberId); + } + + // 가게 이름 필터링 + private BooleanExpression storeNameEq(String storeName) { + return (storeName != null && !storeName.isEmpty()) ? + review.store.storeName.eq(storeName) : null; + } + + // 별점 필터링 + private BooleanExpression ratingEq(String ratingRange) { + if (ratingRange == null || ratingRange.isEmpty()){ + return null; + } + try { + int rating = Integer.parseInt(ratingRange); + if (rating >=1 && rating <= 5 ){ + return review.rating.eq(rating); + } + return null; // 1~5점 범위 밖은 취급하지 않음. + } catch (NumberFormatException e) { + return null; + } + } +} From ed5a4a456536226910442e2368bfd4719a145cb5 Mon Sep 17 00:00:00 2001 From: Sejeong Kim Date: Sun, 9 Nov 2025 22:21:56 +0900 Subject: [PATCH 08/13] =?UTF-8?q?feat:=20=EB=A6=AC=EB=B7=B0=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/review/service/ReviewService.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/service/ReviewService.java diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/service/ReviewService.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/service/ReviewService.java new file mode 100644 index 0000000..22f3729 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/service/ReviewService.java @@ -0,0 +1,21 @@ +package com.example.chapter4_umc.domain.review.service; + +import com.example.chapter4_umc.domain.review.dto.ReviewDto; +import com.example.chapter4_umc.domain.review.dto.ReviewSearchCondition; +import com.example.chapter4_umc.domain.review.repository.ReviewRepositoryCustom; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class ReviewService { + + private final ReviewRepositoryCustom reviewRepositoryCustom; + + public List findMyReviews(Long memberId, ReviewSearchCondition condition) { + return reviewRepositoryCustom.findMyReviewsWithFilter(memberId, condition); + } + +} From 0f0507f154a017f869a24ca59ccac4affe9a3b4e Mon Sep 17 00:00:00 2001 From: Sejeong Kim Date: Sun, 9 Nov 2025 22:22:46 +0900 Subject: [PATCH 09/13] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=EB=90=9C=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=EA=B8=B0=EC=A4=80=20?= =?UTF-8?q?=EB=82=B4=20=EB=A6=AC=EB=B7=B0=20=EC=A1=B0=ED=9A=8C=20=EC=97=94?= =?UTF-8?q?=EB=93=9C=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../review/controller/MyReviewController.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/controller/MyReviewController.java diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/controller/MyReviewController.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/controller/MyReviewController.java new file mode 100644 index 0000000..a9db1b1 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/controller/MyReviewController.java @@ -0,0 +1,39 @@ +package com.example.chapter4_umc.domain.review.controller; + +import com.example.chapter4_umc.domain.review.dto.ReviewDto; +import com.example.chapter4_umc.domain.review.dto.ReviewSearchCondition; +import com.example.chapter4_umc.domain.review.service.ReviewService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import java.util.List; + +@RestController +@RequestMapping("/api/reviews") +@RequiredArgsConstructor +public class MyReviewController { + + private final ReviewService reviewService; + + // 로그인된 사용자 ID 가져오기 + private Long getLoggedInMemberId(){ + return 1L; + } + + @GetMapping("/my") + public List getMyReviews( + @RequestParam(required = false) String storeName, + @RequestParam(required = false) String ratingRange) { + + // 로그인된 사용자 ID 획득 + Long memberId = getLoggedInMemberId(); + + // 검색 조건 DTO 생성 + ReviewSearchCondition condition = new ReviewSearchCondition(); + condition.setStoreName(storeName); + condition.setRatingRange(ratingRange); + + // 서비스 호출 및 결과 반환 + return reviewService.findMyReviews(memberId, condition); + } + +} From 358dc34fb9f10164c357c3a109d40b21c5d03629 Mon Sep 17 00:00:00 2001 From: Sejeong Kim Date: Sun, 16 Nov 2025 23:12:55 +0900 Subject: [PATCH 10/13] =?UTF-8?q?feat:=20API=20=EC=9D=91=EB=8B=B5=20?= =?UTF-8?q?=ED=86=B5=EC=9D=BC=20=EB=B0=8F=20=EC=98=88=EC=99=B8=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../review/controller/MyReviewController.java | 18 +++--- .../review/controller/ReviewController.java | 0 .../review/converter/ReviewConverter.java | 50 ++++++++++++++++ .../review/dto/req/ReviewCreateReqDTO.java | 17 ++++++ .../review/dto/res/ReviewCreateResDTO.java | 14 +++++ .../review/dto/res/ReviewDetailResDTO.java | 28 +++++++++ .../review/dto/res/ReviewListResDTO.java | 15 +++++ .../review/dto/res/ReviewSimpleResDTO.java | 18 ++++++ .../global/apiPayload/ApiResponse.java | 60 +++++++++++++++++++ .../global/apiPayload/code/BaseErrorCode.java | 9 +++ .../apiPayload/code/BaseSuccessCode.java | 10 ++++ .../apiPayload/code/GeneralErrorCode.java | 48 +++++++++++++++ .../apiPayload/code/GeneralSuccessCode.java | 47 +++++++++++++++ .../exception/GeneralException.java | 15 +++++ .../handler/GeneralExceptionAdvice.java | 39 ++++++++++++ 15 files changed, 381 insertions(+), 7 deletions(-) create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/controller/ReviewController.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/converter/ReviewConverter.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/req/ReviewCreateReqDTO.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/res/ReviewCreateResDTO.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/res/ReviewDetailResDTO.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/res/ReviewListResDTO.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/res/ReviewSimpleResDTO.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/ApiResponse.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/BaseErrorCode.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/BaseSuccessCode.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/GeneralErrorCode.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/GeneralSuccessCode.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/exception/GeneralException.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/handler/GeneralExceptionAdvice.java diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/controller/MyReviewController.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/controller/MyReviewController.java index a9db1b1..490e4a0 100644 --- a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/controller/MyReviewController.java +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/controller/MyReviewController.java @@ -3,8 +3,11 @@ import com.example.chapter4_umc.domain.review.dto.ReviewDto; import com.example.chapter4_umc.domain.review.dto.ReviewSearchCondition; import com.example.chapter4_umc.domain.review.service.ReviewService; +import com.example.chapter4_umc.global.apiPayload.ApiResponse; +import com.example.chapter4_umc.global.apiPayload.code.GeneralSuccessCode; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; + import java.util.List; @RestController @@ -14,26 +17,27 @@ public class MyReviewController { private final ReviewService reviewService; - // 로그인된 사용자 ID 가져오기 + // 로그인된 사용자 ID 가져오기 (임시 하드코딩) private Long getLoggedInMemberId(){ return 1L; } @GetMapping("/my") - public List getMyReviews( + public ApiResponse> getMyReviews( @RequestParam(required = false) String storeName, @RequestParam(required = false) String ratingRange) { - // 로그인된 사용자 ID 획득 Long memberId = getLoggedInMemberId(); - // 검색 조건 DTO 생성 ReviewSearchCondition condition = new ReviewSearchCondition(); condition.setStoreName(storeName); condition.setRatingRange(ratingRange); - // 서비스 호출 및 결과 반환 - return reviewService.findMyReviews(memberId, condition); - } + List response = reviewService.findMyReviews(memberId, condition); + return ApiResponse.onSuccess( + GeneralSuccessCode.REVIEW_READ, + response + ); + } } diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/controller/ReviewController.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/controller/ReviewController.java new file mode 100644 index 0000000..e69de29 diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/converter/ReviewConverter.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/converter/ReviewConverter.java new file mode 100644 index 0000000..6457861 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/converter/ReviewConverter.java @@ -0,0 +1,50 @@ +package com.example.chapter4_umc.domain.review.converter; + +import com.example.chapter4_umc.domain.review.dto.res.ReviewCreateResDTO; +import com.example.chapter4_umc.domain.review.dto.res.ReviewDetailResDTO; +import com.example.chapter4_umc.domain.review.dto.res.ReviewListResDTO; +import com.example.chapter4_umc.domain.review.dto.res.ReviewSimpleResDTO; +import com.example.chapter4_umc.domain.review.entity.Review; + +import java.util.ArrayList; +import java.util.List; + +public class ReviewConverter { + + // 리뷰 생성 응답 + public static ReviewCreateResDTO toCreateDTO(Review review){ + return ReviewCreateResDTO.builder() + .reviewId(review.getId()) + .message("리뷰가 성공적으로 생성되었습니다.") + .build(); + } + + public static ReviewDetailResDTO toDetailDTO(Review review){ + return ReviewDetailResDTO.builder() + .reviewId(review.getId()) + .rating(review.getRating()) + .content(review.getContent()) + .imageUrl(review.getImageUrl()) + .createdAt(review.getCreatedAt().toString()) + + .memberId(review.getMember().getId()) + .memberName(review.getMember().getNickname()) + + .storeId(review.getStore().getId()) + .storeName(review.getStore().getStoreName()) + + .regionId(review.getRegion().getId()) + .regionName(review.getRegion().getRegionName()) + .build(); + } + + public static ReviewSimpleResDTO toSimpleDTO(Review review){ + return ReviewSimpleResDTO.builder() + .reviewId(review.getId()) + .rating(review.getRating()) + .content(review.getContent()) + .imageUrl(review.getImageUrl()) + .createdAt(review.getCreatedAt().toString()) + .build(); + } +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/req/ReviewCreateReqDTO.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/req/ReviewCreateReqDTO.java new file mode 100644 index 0000000..b20e7a4 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/req/ReviewCreateReqDTO.java @@ -0,0 +1,17 @@ +package com.example.chapter4_umc.domain.review.dto.req; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class ReviewCreateReqDTO { + + private Integer rating; + private String content; + private String imageUrl; + + private Long memberId; + private Long StoreId; + private Long regionId; +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/res/ReviewCreateResDTO.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/res/ReviewCreateResDTO.java new file mode 100644 index 0000000..117c382 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/res/ReviewCreateResDTO.java @@ -0,0 +1,14 @@ +package com.example.chapter4_umc.domain.review.dto.res; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor +public class ReviewCreateResDTO { + + private Long reviewId; + private String message; +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/res/ReviewDetailResDTO.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/res/ReviewDetailResDTO.java new file mode 100644 index 0000000..c75f136 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/res/ReviewDetailResDTO.java @@ -0,0 +1,28 @@ +package com.example.chapter4_umc.domain.review.dto.res; + +import com.example.chapter4_umc.domain.review.entity.Review; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor +public class ReviewDetailResDTO { + + private Long reviewId; + private Integer rating; + private String content; + private String imageUrl; + private String createdAt; + + private Long memberId; + private String memberName; + + private Long storeId; + private String storeName; + + private Long regionId; + private String regionName; + +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/res/ReviewListResDTO.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/res/ReviewListResDTO.java new file mode 100644 index 0000000..acda2d8 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/res/ReviewListResDTO.java @@ -0,0 +1,15 @@ +package com.example.chapter4_umc.domain.review.dto.res; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@Builder +@AllArgsConstructor +public class ReviewListResDTO { + private List reviews; + private Integer totalCount; +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/res/ReviewSimpleResDTO.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/res/ReviewSimpleResDTO.java new file mode 100644 index 0000000..82fe8cf --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/res/ReviewSimpleResDTO.java @@ -0,0 +1,18 @@ +package com.example.chapter4_umc.domain.review.dto.res; + +import com.example.chapter4_umc.domain.review.entity.Review; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor +public class ReviewSimpleResDTO { + + private Long reviewId; + private Integer rating; + private String content; + private String imageUrl; + private String createdAt; +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/ApiResponse.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/ApiResponse.java new file mode 100644 index 0000000..b390d5f --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/ApiResponse.java @@ -0,0 +1,60 @@ +package com.example.chapter4_umc.global.apiPayload; + +import com.example.chapter4_umc.global.apiPayload.code.BaseErrorCode; +import com.example.chapter4_umc.global.apiPayload.code.BaseSuccessCode; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class ApiResponse { + + @JsonProperty("isSuccess") + private final Boolean isSuccess; + + @JsonProperty("code") + private final String code; + + @JsonProperty("message") + private final String message; + + @JsonProperty("result") + private final T result; + + public static ApiResponse onSuccess(BaseSuccessCode code, T result) { + return new ApiResponse<>( + true, + code.getCode(), + code.getMessage(), + result + ); + } + + public static ApiResponse onSuccess(BaseSuccessCode code){ + return new ApiResponse<>( + true, + code.getCode(), + code.getMessage(), + null + ); + } + + public static ApiResponse onFailure(BaseErrorCode code, T result){ + return new ApiResponse<>( + false, + code.getCode(), + code.getMessage(), + result + ); + } + + public static ApiResponse onFailure(BaseErrorCode code) { + return new ApiResponse<>( + false, + code.getCode(), + code.getMessage(), + null + ); + } +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/BaseErrorCode.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/BaseErrorCode.java new file mode 100644 index 0000000..bc06752 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/BaseErrorCode.java @@ -0,0 +1,9 @@ +package com.example.chapter4_umc.global.apiPayload.code; + +import org.springframework.http.HttpStatus; + +public interface BaseErrorCode { + HttpStatus getStatus(); + String getCode(); + String getMessage(); +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/BaseSuccessCode.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/BaseSuccessCode.java new file mode 100644 index 0000000..61e17a1 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/BaseSuccessCode.java @@ -0,0 +1,10 @@ +package com.example.chapter4_umc.global.apiPayload.code; + +import org.springframework.boot.autoconfigure.graphql.GraphQlProperties; +import org.springframework.http.HttpStatus; + +public interface BaseSuccessCode { + HttpStatus getStatus(); + String getCode(); + String getMessage(); +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/GeneralErrorCode.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/GeneralErrorCode.java new file mode 100644 index 0000000..ce64b7d --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/GeneralErrorCode.java @@ -0,0 +1,48 @@ +package com.example.chapter4_umc.global.apiPayload.code; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum GeneralErrorCode implements BaseErrorCode{ + + BAD_REQUEST(HttpStatus.BAD_REQUEST, + "COMMON400_1", + "잘못된 요청입니다."), + + UNAUTHORIZED(HttpStatus.UNAUTHORIZED, + "AUTH401_1", + "인증이 필요합니다."), + + FORBIDDEN(HttpStatus.FORBIDDEN, + "AUTH403_1", + "요청이 거부되었습니다."), + + NOT_FOUND(HttpStatus.NOT_FOUND, + "COMMON404_1", + "요청한 리소스를 찾을 수 없습니다."), + + INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, + "COMMON500_1", + "예기치 않은 서버 에러가 발생했습니다."), + + REVIEW_NOT_FOUND(HttpStatus.NOT_FOUND, + "REVIEW404_1", + "해당 리뷰를 찾을 수 없습니다."), + + REVIEW_FORBIDEN(HttpStatus.FORBIDDEN, + "REVIEW403_1", + "해당 리뷰에 접근할 권한이 없습니다."), + + REVIEW_BAD_REQUEST(HttpStatus.BAD_REQUEST, + "REVIEW400_1", + "잘못된 리뷰 요청입니다.") + ; + + private final HttpStatus status; + private final String code; + private final String message; + +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/GeneralSuccessCode.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/GeneralSuccessCode.java new file mode 100644 index 0000000..9d818f0 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/GeneralSuccessCode.java @@ -0,0 +1,47 @@ +package com.example.chapter4_umc.global.apiPayload.code; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum GeneralSuccessCode implements BaseSuccessCode { + + OK(HttpStatus.OK, + "SUCCESS200_1", + "요청이 성공적으로 처리되었습니다."), + + CREATED(HttpStatus.CREATED, + "SUCCESS201_1", + "리소스가 성공적으로 생성되었습니다."), + + UPDATED(HttpStatus.OK, + "SUCCESS200_2", + "리소스가 성공적으로 수정되었습니다."), + + DELETED(HttpStatus.OK, + "SUCCESS200_3", + "리소스가 성공적으로 삭제되었습니다."), + + REVIEW_READ(HttpStatus.OK, + "REVIEW200_1", + "리뷰 조회 성공"), + + REVIEW_CREATED(HttpStatus.CREATED, + "REVIEW201_1", + "리뷰가 성공적으로 생성되었습니다."), + + REVIEW_UPDATED(HttpStatus.OK, + "REVIEW200_2", + "리뷰 수정 성공"), + + REVIEW_DELETED(HttpStatus.OK, + "REVIEW200_3", + "리뷰 삭제 성공") + ; + + private final HttpStatus status; + private final String code; + private final String message; +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/exception/GeneralException.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/exception/GeneralException.java new file mode 100644 index 0000000..667fa37 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/exception/GeneralException.java @@ -0,0 +1,15 @@ +package com.example.chapter4_umc.global.apiPayload.exception; + +import com.example.chapter4_umc.global.apiPayload.code.BaseErrorCode; +import lombok.Getter; + +@Getter +public class GeneralException extends RuntimeException{ + + private final BaseErrorCode code; + + public GeneralException(BaseErrorCode code) { + super(code.getMessage()); + this.code = code; + } +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/handler/GeneralExceptionAdvice.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/handler/GeneralExceptionAdvice.java new file mode 100644 index 0000000..261a04a --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/handler/GeneralExceptionAdvice.java @@ -0,0 +1,39 @@ +package com.example.chapter4_umc.global.apiPayload.handler; + +import com.example.chapter4_umc.global.apiPayload.ApiResponse; +import com.example.chapter4_umc.global.apiPayload.code.BaseErrorCode; +import com.example.chapter4_umc.global.apiPayload.code.GeneralErrorCode; +import com.example.chapter4_umc.global.apiPayload.exception.GeneralException; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GeneralExceptionAdvice { + + @ExceptionHandler(GeneralException.class) + public ResponseEntity> handleException ( + GeneralException ex + ) { + return ResponseEntity.status(ex.getCode().getStatus()) + .body(ApiResponse.onFailure( + ex.getCode(), + null + ) + ); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity> handleException ( + Exception ex + ) { + + BaseErrorCode code = GeneralErrorCode.INTERNAL_SERVER_ERROR; + return ResponseEntity.status(code.getStatus()) + .body(ApiResponse.onFailure( + code, + null + ) + ); + } +} From 51df08e20458a1b09a87894f785716f4510a402f Mon Sep 17 00:00:00 2001 From: Sejeong Kim Date: Sun, 23 Nov 2025 21:17:13 +0900 Subject: [PATCH 11/13] =?UTF-8?q?feat:=20=EB=A6=AC=EB=B7=B0=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20DTO=20Swagger=20=EB=AC=B8=EC=84=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4_umc/build.gradle | 1 + .../review/dto/req/ReviewCreateReqDTO.java | 20 ++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/chapter4_umc/build.gradle b/chapter4_umc/build.gradle index f7e4171..86f1c37 100644 --- a/chapter4_umc/build.gradle +++ b/chapter4_umc/build.gradle @@ -39,6 +39,7 @@ dependencies { annotationProcessor "io.github.openfeign.querydsl:querydsl-apt:7.0:jpa" annotationProcessor "jakarta.persistence:jakarta.persistence-api" annotationProcessor "jakarta.annotation:jakarta.annotation-api" + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0' } tasks.named('test') { diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/req/ReviewCreateReqDTO.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/req/ReviewCreateReqDTO.java index b20e7a4..be790b3 100644 --- a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/req/ReviewCreateReqDTO.java +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/dto/req/ReviewCreateReqDTO.java @@ -1,5 +1,6 @@ package com.example.chapter4_umc.domain.review.dto.req; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import lombok.NoArgsConstructor; @@ -7,11 +8,28 @@ @NoArgsConstructor public class ReviewCreateReqDTO { + + @Schema(description = "평점 (1~5)", example = "5") private Integer rating; + + @Schema(description = "리뷰 내용", example = "음식이 맛있어요") private String content; + + @Schema(description = "이미지 URL", example = "https://example.com/image.jpg") private String imageUrl; private Long memberId; - private Long StoreId; + + private Long storeId; + + @Schema(description = "지역 ID", example = "2") private Long regionId; + + public void setMemberId(Long memberId) { + this.memberId = memberId; + } + + public void setStoreId(Long storeId) { + this.storeId = storeId; + } } From 5843827aa02b6a9a01b23cb58ab0536859db02dc Mon Sep 17 00:00:00 2001 From: Sejeong Kim Date: Sun, 23 Nov 2025 21:35:42 +0900 Subject: [PATCH 12/13] =?UTF-8?q?feat:=20=EB=A6=AC=EB=B7=B0=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20api=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/repository/MemberRepository.java | 7 +++ .../domain/mission/dto/MissionJoinResDTO.java | 13 ++++++ .../region/repository/RegionRepository.java | 7 +++ .../controller/ReviewCreateController.java | 44 +++++++++++++++++++ .../review/repository/ReviewRepository.java | 7 +++ .../domain/review/service/ReviewService.java | 37 ++++++++++++++++ .../store/repository/StoreRepository.java | 7 +++ 7 files changed, 122 insertions(+) create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/member/repository/MemberRepository.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/dto/MissionJoinResDTO.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/region/repository/RegionRepository.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/controller/ReviewCreateController.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/repository/ReviewRepository.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/store/repository/StoreRepository.java diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/member/repository/MemberRepository.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/member/repository/MemberRepository.java new file mode 100644 index 0000000..74b2c04 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/member/repository/MemberRepository.java @@ -0,0 +1,7 @@ +package com.example.chapter4_umc.domain.member.repository; + +import com.example.chapter4_umc.domain.member.entity.Member; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MemberRepository extends JpaRepository { +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/dto/MissionJoinResDTO.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/dto/MissionJoinResDTO.java new file mode 100644 index 0000000..6cc2e14 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/dto/MissionJoinResDTO.java @@ -0,0 +1,13 @@ +package com.example.chapter4_umc.domain.mission.dto; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class MissionJoinResDTO { + + private Long missionId; + private Long memberId; + private String status; +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/region/repository/RegionRepository.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/region/repository/RegionRepository.java new file mode 100644 index 0000000..8029a8d --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/region/repository/RegionRepository.java @@ -0,0 +1,7 @@ +package com.example.chapter4_umc.domain.region.repository; + +import com.example.chapter4_umc.domain.region.entity.Region; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface RegionRepository extends JpaRepository { +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/controller/ReviewCreateController.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/controller/ReviewCreateController.java new file mode 100644 index 0000000..62aca1a --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/controller/ReviewCreateController.java @@ -0,0 +1,44 @@ +package com.example.chapter4_umc.domain.review.controller; + +import com.example.chapter4_umc.domain.review.dto.req.ReviewCreateReqDTO; +import com.example.chapter4_umc.domain.review.dto.res.ReviewCreateResDTO; +import com.example.chapter4_umc.domain.review.service.ReviewService; +import com.example.chapter4_umc.global.apiPayload.ApiResponse; +import com.example.chapter4_umc.global.apiPayload.code.GeneralSuccessCode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1") +@Tag(name = "Review", description = "리뷰 작성 API") +public class ReviewCreateController { + + private final ReviewService reviewService; + + private Long getLoggedInMemberId() { + return 1L; + } + + @Operation( + summary = "가게 리뷰 작성", + description = "특정 가게에 리뷰를 작성합니다." + ) + @PostMapping("/{storeId}/reviews") + public ApiResponse createReview( + @PathVariable Long storeId, + @RequestBody ReviewCreateReqDTO req + ) { + req.setStoreId(storeId); + req.setMemberId(getLoggedInMemberId()); + + ReviewCreateResDTO response = reviewService.createReview(req); + + return ApiResponse.onSuccess( + GeneralSuccessCode.REVIEW_CREATED, + response + ); + } +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/repository/ReviewRepository.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/repository/ReviewRepository.java new file mode 100644 index 0000000..947959a --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/repository/ReviewRepository.java @@ -0,0 +1,7 @@ +package com.example.chapter4_umc.domain.review.repository; + +import com.example.chapter4_umc.domain.review.entity.Review; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ReviewRepository extends JpaRepository { +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/service/ReviewService.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/service/ReviewService.java index 22f3729..1e6352d 100644 --- a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/service/ReviewService.java +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/review/service/ReviewService.java @@ -1,8 +1,20 @@ package com.example.chapter4_umc.domain.review.service; +import com.example.chapter4_umc.domain.member.entity.Member; +import com.example.chapter4_umc.domain.member.repository.MemberRepository; +import com.example.chapter4_umc.domain.region.entity.Region; +import com.example.chapter4_umc.domain.region.repository.RegionRepository; +import com.example.chapter4_umc.domain.review.converter.ReviewConverter; import com.example.chapter4_umc.domain.review.dto.ReviewDto; import com.example.chapter4_umc.domain.review.dto.ReviewSearchCondition; +import com.example.chapter4_umc.domain.review.dto.req.ReviewCreateReqDTO; +import com.example.chapter4_umc.domain.review.dto.res.ReviewCreateResDTO; +import com.example.chapter4_umc.domain.review.entity.Review; +import com.example.chapter4_umc.domain.review.repository.ReviewRepository; import com.example.chapter4_umc.domain.review.repository.ReviewRepositoryCustom; +import com.example.chapter4_umc.domain.store.entity.Store; +import com.example.chapter4_umc.domain.store.repository.StoreRepository; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -12,8 +24,33 @@ @RequiredArgsConstructor public class ReviewService { + private final ReviewRepository reviewRepository; + private final MemberRepository memberRepository; + private final StoreRepository storeRepository; + private final RegionRepository regionRepository; private final ReviewRepositoryCustom reviewRepositoryCustom; + @Transactional + public ReviewCreateResDTO createReview(ReviewCreateReqDTO req) { + + Member member = memberRepository.findById(req.getMemberId()).orElseThrow(); + Store store = storeRepository.findById(req.getStoreId()).orElseThrow(); + Region region = regionRepository.findById(req.getRegionId()).orElseThrow(); + + Review review = Review.builder() + .rating(req.getRating()) + .content(req.getContent()) + .imageUrl(req.getImageUrl()) + .member(member) + .store(store) + .region(region) + .build(); + + reviewRepository.save(review); + + return ReviewConverter.toCreateDTO(review); + } + public List findMyReviews(Long memberId, ReviewSearchCondition condition) { return reviewRepositoryCustom.findMyReviewsWithFilter(memberId, condition); } diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/store/repository/StoreRepository.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/store/repository/StoreRepository.java new file mode 100644 index 0000000..6974fe2 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/store/repository/StoreRepository.java @@ -0,0 +1,7 @@ +package com.example.chapter4_umc.domain.store.repository; + +import com.example.chapter4_umc.domain.store.entity.Store; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface StoreRepository extends JpaRepository { +} From e18dd1740b1a061688f31167624c4d52b665ca27 Mon Sep 17 00:00:00 2001 From: Sejeong Kim Date: Sun, 23 Nov 2025 23:20:54 +0900 Subject: [PATCH 13/13] =?UTF-8?q?feat:=20=EB=AF=B8=EC=85=98=20=EB=8F=84?= =?UTF-8?q?=EC=A0=84=20API=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mission/controller/MissionController.java | 36 +++++++++++++++++ .../mission/entity/MissionProgress.java | 35 ++++++++++++++++ .../repository/MissionProgressRepository.java | 7 ++++ .../mission/repository/MissionRepository.java | 7 ++++ .../mission/service/MissionService.java | 40 +++++++++++++++++++ .../apiPayload/code/GeneralSuccessCode.java | 6 ++- 6 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/controller/MissionController.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/entity/MissionProgress.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/repository/MissionProgressRepository.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/repository/MissionRepository.java create mode 100644 chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/service/MissionService.java diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/controller/MissionController.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/controller/MissionController.java new file mode 100644 index 0000000..e3d3536 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/controller/MissionController.java @@ -0,0 +1,36 @@ +package com.example.chapter4_umc.domain.mission.controller; + +import com.example.chapter4_umc.domain.mission.dto.MissionJoinResDTO; +import com.example.chapter4_umc.domain.mission.service.MissionService; +import com.example.chapter4_umc.global.apiPayload.ApiResponse; +import com.example.chapter4_umc.global.apiPayload.code.GeneralSuccessCode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/missions") +@Tag(name = "Mission", description = "미션 도전 API") +public class MissionController { + + private final MissionService missionService; + + private Long getLoggedInMemberId() { + return 1L; // 하드코딩 + } + + @Operation(summary = "미션 도전하기", description = "특정 미션을 도전합니다.") + @PostMapping("/{missionId}/join") + public ApiResponse joinMission( + @PathVariable Long missionId + ) { + MissionJoinResDTO res = missionService.joinMission(missionId, getLoggedInMemberId()); + + return ApiResponse.onSuccess( + GeneralSuccessCode.MISSION_JOIN, + res + ); + } +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/entity/MissionProgress.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/entity/MissionProgress.java new file mode 100644 index 0000000..30f446d --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/entity/MissionProgress.java @@ -0,0 +1,35 @@ +package com.example.chapter4_umc.domain.mission.entity; + +import com.example.chapter4_umc.domain.member.entity.Member; +import jakarta.persistence.*; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor +public class MissionProgress { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "mission_progress_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "mission_id") + private Mission mission; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + private String status; + + @Builder + public MissionProgress(Mission mission, Member member, String status) { + this.mission = mission; + this.member = member; + this.status = status; + } +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/repository/MissionProgressRepository.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/repository/MissionProgressRepository.java new file mode 100644 index 0000000..75be664 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/repository/MissionProgressRepository.java @@ -0,0 +1,7 @@ +package com.example.chapter4_umc.domain.mission.repository; + +import com.example.chapter4_umc.domain.mission.entity.MissionProgress; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MissionProgressRepository extends JpaRepository { +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/repository/MissionRepository.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/repository/MissionRepository.java new file mode 100644 index 0000000..ff16ca1 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/repository/MissionRepository.java @@ -0,0 +1,7 @@ +package com.example.chapter4_umc.domain.mission.repository; + +import com.example.chapter4_umc.domain.mission.entity.Mission; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MissionRepository extends JpaRepository { +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/service/MissionService.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/service/MissionService.java new file mode 100644 index 0000000..ceb83d9 --- /dev/null +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/domain/mission/service/MissionService.java @@ -0,0 +1,40 @@ +package com.example.chapter4_umc.domain.mission.service; + +import com.example.chapter4_umc.domain.member.entity.Member; +import com.example.chapter4_umc.domain.member.repository.MemberRepository; +import com.example.chapter4_umc.domain.mission.dto.MissionJoinResDTO; +import com.example.chapter4_umc.domain.mission.entity.Mission; +import com.example.chapter4_umc.domain.mission.entity.MissionProgress; +import com.example.chapter4_umc.domain.mission.repository.MissionProgressRepository; +import com.example.chapter4_umc.domain.mission.repository.MissionRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class MissionService { + + private final MissionRepository missionRepository; + private final MissionProgressRepository missionProgressRepository; + private final MemberRepository memberRepository; + + public MissionJoinResDTO joinMission(Long missionId, Long memberId) { + + Mission mission = missionRepository.findById(missionId).orElseThrow(); + Member member = memberRepository.findById(memberId).orElseThrow(); + + MissionProgress progress = MissionProgress.builder() + .mission(mission) + .member(member) + .status("도전중") + .build(); + + missionProgressRepository.save(progress); + + return MissionJoinResDTO.builder() + .missionId(missionId) + .memberId(memberId) + .status("도전중") + .build(); + } +} diff --git a/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/GeneralSuccessCode.java b/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/GeneralSuccessCode.java index 9d818f0..d623494 100644 --- a/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/GeneralSuccessCode.java +++ b/chapter4_umc/src/main/java/com/example/chapter4_umc/global/apiPayload/code/GeneralSuccessCode.java @@ -38,7 +38,11 @@ public enum GeneralSuccessCode implements BaseSuccessCode { REVIEW_DELETED(HttpStatus.OK, "REVIEW200_3", - "리뷰 삭제 성공") + "리뷰 삭제 성공"), + + MISSION_JOIN(HttpStatus.OK, + "MISSION200_1", + "미션 도전 성공") ; private final HttpStatus status;