From d3b556f0a53141c46d4f9b9770882df509557ed5 Mon Sep 17 00:00:00 2001 From: yummygyudon Date: Wed, 11 Dec 2024 11:07:54 +0900 Subject: [PATCH 01/17] =?UTF-8?q?[FEAT]=20=EB=B0=B0=EB=84=88=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20API=20Response=20DTO=20?= =?UTF-8?q?=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../banner/dto/response/BannerResponse.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 operation-api/src/main/java/org/sopt/makers/operation/web/banner/dto/response/BannerResponse.java diff --git a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/dto/response/BannerResponse.java b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/dto/response/BannerResponse.java new file mode 100644 index 00000000..6921c7e0 --- /dev/null +++ b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/dto/response/BannerResponse.java @@ -0,0 +1,26 @@ +package org.sopt.makers.operation.web.banner.dto.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.RequiredArgsConstructor; + +import java.time.LocalDate; + +import static lombok.AccessLevel.PRIVATE; + +@RequiredArgsConstructor(access = PRIVATE) +public final class BannerResponse { + + public record BannerDetail( + @JsonProperty("id") long bannerId, + @JsonProperty("status") String bannerStatus, + @JsonProperty("location") String bannerLocation, + @JsonProperty("content_type") String bannerType, + @JsonProperty("publisher") String publisher, + @JsonProperty("start_date") LocalDate startDate, + @JsonProperty("end_date") LocalDate endDate, + @JsonProperty("image_url_pc") String pcImageUrl, + @JsonProperty("image_url_mobile") String mobileImageUrl + ) { + + } +} From 6260e8907f4708be729f5f067cc7c81e0d00cec0 Mon Sep 17 00:00:00 2001 From: yummygyudon Date: Wed, 11 Dec 2024 11:12:55 +0900 Subject: [PATCH 02/17] =?UTF-8?q?[FEAT]=20Banner=20=EC=83=81=EC=84=B8=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=97=B0=EC=82=B0=20Service=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../operation/web/banner/service/BannerService.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 operation-api/src/main/java/org/sopt/makers/operation/web/banner/service/BannerService.java diff --git a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/service/BannerService.java b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/service/BannerService.java new file mode 100644 index 00000000..475b8bc3 --- /dev/null +++ b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/service/BannerService.java @@ -0,0 +1,8 @@ +package org.sopt.makers.operation.web.banner.service; + +import org.sopt.makers.operation.web.banner.dto.response.BannerResponse; + +public interface BannerService { + + BannerResponse.BannerDetail getBannerDetail(final long bannerId); +} From a2459f035f1b7afc422a6b376ac601ad5606a98f Mon Sep 17 00:00:00 2001 From: yummygyudon Date: Wed, 11 Dec 2024 11:14:00 +0900 Subject: [PATCH 03/17] =?UTF-8?q?[FEAT]=20Banner=20=EC=83=81=EC=84=B8=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20API=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 조회 성공 상태에 대한 Success Status Code Enum 추가 구현했습니다. (`BannerSuccessCode`) --- .../operation/web/banner/api/BannerApi.java | 34 +++++++++++++++++++ .../web/banner/api/BannerApiController.java | 31 +++++++++++++++++ .../code/success/web/BannerSuccessCode.java | 19 +++++++++++ 3 files changed, 84 insertions(+) create mode 100644 operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApi.java create mode 100644 operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApiController.java create mode 100644 operation-common/src/main/java/org/sopt/makers/operation/code/success/web/BannerSuccessCode.java diff --git a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApi.java b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApi.java new file mode 100644 index 00000000..aa7b52bf --- /dev/null +++ b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApi.java @@ -0,0 +1,34 @@ +package org.sopt.makers.operation.web.banner.api; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; + +import org.sopt.makers.operation.dto.BaseResponse; + +import org.springframework.http.ResponseEntity; + +public interface BannerApi { + @Operation( + summary = "배너 상세 조회 API", + responses = { + @ApiResponse( + responseCode = "200", + description = "배너 상세 조회 성공" + ), + @ApiResponse( + responseCode = "400", + description = "잘못된 요청" + ), + @ApiResponse( + responseCode = "404", + description = "존재하지 않는 배너 ID 요청" + ), + @ApiResponse( + responseCode = "500", + description = "서버 내부 오류" + ) + } + ) + ResponseEntity> getBannerDetail(Long bannerId); + +} diff --git a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApiController.java b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApiController.java new file mode 100644 index 00000000..606aa7a8 --- /dev/null +++ b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApiController.java @@ -0,0 +1,31 @@ +package org.sopt.makers.operation.web.banner.api; + +import lombok.RequiredArgsConstructor; +import lombok.val; +import org.sopt.makers.operation.code.success.web.BannerSuccessCode; +import org.sopt.makers.operation.dto.BaseResponse; +import org.sopt.makers.operation.util.ApiResponseUtil; +import org.sopt.makers.operation.web.banner.service.BannerService; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import static org.sopt.makers.operation.code.success.web.BannerSuccessCode.SUCCESS_GET_BANNER_DETAIL; + +@RestController +@RequestMapping("/api/v1/banners") +@RequiredArgsConstructor +public class BannerApiController implements BannerApi { + private final BannerService bannerService; + + @Override + @GetMapping("/{bannerId}") + public ResponseEntity> getBannerDetail( + @PathVariable("bannerId") Long bannerId + ) { + val response = bannerService.getBannerDetail(bannerId); + return ApiResponseUtil.success(SUCCESS_GET_BANNER_DETAIL, response); + } +} diff --git a/operation-common/src/main/java/org/sopt/makers/operation/code/success/web/BannerSuccessCode.java b/operation-common/src/main/java/org/sopt/makers/operation/code/success/web/BannerSuccessCode.java new file mode 100644 index 00000000..c5e186b4 --- /dev/null +++ b/operation-common/src/main/java/org/sopt/makers/operation/code/success/web/BannerSuccessCode.java @@ -0,0 +1,19 @@ +package org.sopt.makers.operation.code.success.web; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.sopt.makers.operation.code.success.SuccessCode; +import org.springframework.http.HttpStatus; + +import static lombok.AccessLevel.PRIVATE; + +@Getter +@RequiredArgsConstructor(access = PRIVATE) +public enum BannerSuccessCode implements SuccessCode { + SUCCESS_GET_BANNER_DETAIL(HttpStatus.OK, "배너 상세 정보 조회 성공"), + + ; + private final HttpStatus status; + private final String message; +} From d47498cd9efac5ed6aaca54a0ebbb08bfe5b17a6 Mon Sep 17 00:00:00 2001 From: yummygyudon Date: Thu, 12 Dec 2024 03:41:44 +0900 Subject: [PATCH 04/17] =?UTF-8?q?[FEAT]=20Banner=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=20=EA=B5=AC=ED=98=84=20(Exception=20&=20Fail?= =?UTF-8?q?ure=20Code=20Enum)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../code/failure/BannerFailureCode.java | 20 +++++++++++++++++++ .../operation/exception/BannerException.java | 15 ++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 operation-common/src/main/java/org/sopt/makers/operation/code/failure/BannerFailureCode.java create mode 100644 operation-common/src/main/java/org/sopt/makers/operation/exception/BannerException.java diff --git a/operation-common/src/main/java/org/sopt/makers/operation/code/failure/BannerFailureCode.java b/operation-common/src/main/java/org/sopt/makers/operation/code/failure/BannerFailureCode.java new file mode 100644 index 00000000..7a13f4b2 --- /dev/null +++ b/operation-common/src/main/java/org/sopt/makers/operation/code/failure/BannerFailureCode.java @@ -0,0 +1,20 @@ +package org.sopt.makers.operation.code.failure; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +import static org.springframework.http.HttpStatus.NOT_FOUND; + +@RequiredArgsConstructor +@Getter +public enum BannerFailureCode implements FailureCode { + NOT_FOUND_STATUS(NOT_FOUND, "존재하지 않는 게시 상태입니다."), + NOT_FOUND_LOCATION(NOT_FOUND, "존재하지 않는 게시 위치입니다."), + NOT_FOUND_CONTENT_TYPE(NOT_FOUND, "존재하지 않는 게시 유형입니다."), + NOT_FOUNT_BANNER(NOT_FOUND, "존재하지 않는 배너입니다."), + ; + + private final HttpStatus status; + private final String message; +} diff --git a/operation-common/src/main/java/org/sopt/makers/operation/exception/BannerException.java b/operation-common/src/main/java/org/sopt/makers/operation/exception/BannerException.java new file mode 100644 index 00000000..1bc29ac2 --- /dev/null +++ b/operation-common/src/main/java/org/sopt/makers/operation/exception/BannerException.java @@ -0,0 +1,15 @@ +package org.sopt.makers.operation.exception; + +import lombok.Getter; +import org.sopt.makers.operation.code.failure.FailureCode; + +@Getter +public class BannerException extends RuntimeException { + private final FailureCode failureCode; + + public BannerException(FailureCode failureCode) { + super("[BannerException] : " + failureCode.getMessage()); + this.failureCode = failureCode; + } + +} From e198d1c485789ebfc81d04e26e5487916a6bc99a Mon Sep 17 00:00:00 2001 From: yummygyudon Date: Thu, 12 Dec 2024 03:45:31 +0900 Subject: [PATCH 05/17] =?UTF-8?q?[FEAT]=20Banner=20=EA=B5=AC=EC=84=B1=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=20=EC=9A=94=EC=86=8C=20Enum=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 소문자인 외부 반환 데이터를 관리하는 필드 `value`을 선언했습니다. 또한 외부 조회 등에 사용될 경우, `value`를 통해 조회할 수 있도록 하기 위해 `getByValue` 메서드를 정의했고 조회하려는 값에 해당하는 Enum이 없을 경우 Banner Exception이 발생하도록 했습니다. - ContentType : 게시물 유형 - PublishLocation : 게시 위치 - PublishStatus : 게시 상태 --- .../operation/banner/domain/ContentType.java | 30 +++++++++++++++++++ .../banner/domain/PublishLocation.java | 28 +++++++++++++++++ .../banner/domain/PublishStatus.java | 27 +++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/ContentType.java create mode 100644 operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishLocation.java create mode 100644 operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishStatus.java diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/ContentType.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/ContentType.java new file mode 100644 index 00000000..02db9079 --- /dev/null +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/ContentType.java @@ -0,0 +1,30 @@ +package org.sopt.makers.operation.banner.domain; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.sopt.makers.operation.code.failure.BannerFailureCode; +import org.sopt.makers.operation.exception.BannerException; + +import java.util.Arrays; + +import static lombok.AccessLevel.PRIVATE; + +@Getter +@RequiredArgsConstructor(access = PRIVATE) +public enum ContentType { + PRODUCT("product"), + BIRTHDAY("birthday"), + SPONSOR("sponsor"), + EVENT("event"), + ETC("etc"), + ; + + private final String value; + + public static PublishLocation getByValue(String value) { + return Arrays.stream(PublishLocation.values()) + .filter(location -> location.getValue().equals(value)) + .findAny().orElseThrow(() -> new BannerException(BannerFailureCode.NOT_FOUND_CONTENT_TYPE)); + } +} diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishLocation.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishLocation.java new file mode 100644 index 00000000..bab1e832 --- /dev/null +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishLocation.java @@ -0,0 +1,28 @@ +package org.sopt.makers.operation.banner.domain; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.sopt.makers.operation.code.failure.BannerFailureCode; +import org.sopt.makers.operation.exception.BannerException; + +import java.util.Arrays; + +import static lombok.AccessLevel.PRIVATE; + +@Getter +@RequiredArgsConstructor(access = PRIVATE) +public enum PublishLocation { + PLAYGROUND_COMMUNITY("pg_community"), + CREW_MAIN("cr_main"), + CREW_FEED("cr_feed"), + OFFICIAL_PAGE("org"), + ; + private final String value; + + public static PublishLocation getByValue(String value) { + return Arrays.stream(PublishLocation.values()) + .filter(location -> location.getValue().equals(value)) + .findAny().orElseThrow(() -> new BannerException(BannerFailureCode.NOT_FOUND_LOCATION)); + } +} diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishStatus.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishStatus.java new file mode 100644 index 00000000..5953094a --- /dev/null +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishStatus.java @@ -0,0 +1,27 @@ +package org.sopt.makers.operation.banner.domain; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.sopt.makers.operation.code.failure.BannerFailureCode; +import org.sopt.makers.operation.exception.BannerException; + +import java.util.Arrays; + +import static lombok.AccessLevel.PRIVATE; + +@Getter +@RequiredArgsConstructor(access = PRIVATE) +public enum PublishStatus { + RESERVED("reserved"), + IN_PROGRESS("in_progress"), + DONE("done"), + ; + private final String value; + + public static PublishLocation getByValue(String value) { + return Arrays.stream(PublishLocation.values()) + .filter(location -> location.getValue().equals(value)) + .findAny().orElseThrow(() -> new BannerException(BannerFailureCode.NOT_FOUND_STATUS)); + } +} From cd1c4de71ee59c0fae0d3e71180d8154e50b6d0e Mon Sep 17 00:00:00 2001 From: yummygyudon Date: Thu, 12 Dec 2024 03:48:40 +0900 Subject: [PATCH 06/17] =?UTF-8?q?[FEAT]=20Embeddable=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=A0=95=EC=9D=98=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 단일 Entity 내에 모든 필드를 관리할 경우, 관리 및 유지보수에 어려움이 있을 것이며 관심사가 혼재될 것을 우려하여 Embeddable 객체로 분리하여 정의했습니다. - `BannerImage` : PC/Mobile 용 이미지 객체 - `PublishPeriod` : 게시 기간 객체 - 게시 시작 날짜 & 게시 종료 날짜 기반으로 `PublishStatus`를 연산하여 반환하는 메서드 `getPublishStatus`를 정의했습니다. --- .../operation/banner/domain/BannerImage.java | 27 +++++++++++++ .../banner/domain/PublishPeriod.java | 38 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/BannerImage.java create mode 100644 operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishPeriod.java diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/BannerImage.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/BannerImage.java new file mode 100644 index 00000000..c4321bc6 --- /dev/null +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/BannerImage.java @@ -0,0 +1,27 @@ +package org.sopt.makers.operation.banner.domain; + +import jakarta.persistence.Embeddable; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import static lombok.AccessLevel.PROTECTED; + +@Getter +@Embeddable +@NoArgsConstructor(access = PROTECTED) +@AllArgsConstructor +public class BannerImage { + private String pcImageUrl; + private String mobileImageUrl; + + public void changePcImageTo(String changedPcImageUrl) { + this.pcImageUrl = changedPcImageUrl; + } + + public void changeMobileImageTo(String changedMobileImageUrl) { + this.pcImageUrl = changedMobileImageUrl; + } + +} diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishPeriod.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishPeriod.java new file mode 100644 index 00000000..c0b9dd2e --- /dev/null +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishPeriod.java @@ -0,0 +1,38 @@ +package org.sopt.makers.operation.banner.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import lombok.*; + +import java.time.LocalDate; + +import static lombok.AccessLevel.PROTECTED; + +@Getter +@Embeddable +@NoArgsConstructor(access = PROTECTED) +@AllArgsConstructor +public class PublishPeriod { + private LocalDate startDate; + private LocalDate endDate; + + public void changeStartDateTo(LocalDate changedDate) { + this.startDate = changedDate; + } + + public void changeEndDateTo(LocalDate changedDate) { + this.endDate = changedDate; + } + + public PublishStatus getPublishStatus(LocalDate date) { + if (date.isAfter(endDate)) { + return PublishStatus.DONE; + } else { + if (date.isAfter(startDate)) { + return PublishStatus.IN_PROGRESS; + } + return PublishStatus.RESERVED; + } + } + +} From 8bd342cd1845025f47e2880040b184509cd128bd Mon Sep 17 00:00:00 2001 From: yummygyudon Date: Thu, 12 Dec 2024 03:50:06 +0900 Subject: [PATCH 07/17] =?UTF-8?q?[TEST]=20PublishPeriod=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20PublishStatus=20=EC=97=B0=EC=82=B0=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../operation/banner/PublishPeriodTest.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 operation-domain/src/test/java/org/sopt/makers/operation/banner/PublishPeriodTest.java diff --git a/operation-domain/src/test/java/org/sopt/makers/operation/banner/PublishPeriodTest.java b/operation-domain/src/test/java/org/sopt/makers/operation/banner/PublishPeriodTest.java new file mode 100644 index 00000000..f0d6a3da --- /dev/null +++ b/operation-domain/src/test/java/org/sopt/makers/operation/banner/PublishPeriodTest.java @@ -0,0 +1,45 @@ +package org.sopt.makers.operation.banner; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import org.sopt.makers.operation.banner.domain.PublishPeriod; +import org.sopt.makers.operation.banner.domain.PublishStatus; + +import java.time.LocalDate; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PublishPeriodTest { + + private static final LocalDate TEST_START_DATE = LocalDate.of(2024,1,1); + private static final LocalDate TEST_END_DATE = LocalDate.of(2024,12,31); + private final PublishPeriod givenPeriod = new PublishPeriod(TEST_START_DATE, TEST_END_DATE); + + @ParameterizedTest(name = "({index}) date : {0} -> result : {1}") + @MethodSource("argsForCalculateStatus") + @DisplayName("[TEST] 기간에 대한 상태 연산") + void calculateCorrectStatus( + // given + LocalDate givenDate, + PublishStatus expectedStatus + ) { + // when + PublishStatus resultStatus = givenPeriod.getPublishStatus(givenDate); + + // then + assertThat(resultStatus).isEqualTo(expectedStatus); + } + + static Stream argsForCalculateStatus() { + return Stream.of( + Arguments.of(LocalDate.of(2023,12,31), PublishStatus.RESERVED), + Arguments.of(LocalDate.of(2024,6,30), PublishStatus.IN_PROGRESS), + Arguments.of(LocalDate.of(2025,1,1), PublishStatus.DONE) + ); + } + +} From a45154fe7a43f95e8c2e972fd3ca2400077cbba9 Mon Sep 17 00:00:00 2001 From: yummygyudon Date: Thu, 12 Dec 2024 03:55:21 +0900 Subject: [PATCH 08/17] =?UTF-8?q?[STYLE]=20=EA=B0=92=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=AA=85=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(`change`=20->=20`update`)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sopt/makers/operation/banner/domain/BannerImage.java | 8 ++++---- .../makers/operation/banner/domain/PublishPeriod.java | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/BannerImage.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/BannerImage.java index c4321bc6..5081e859 100644 --- a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/BannerImage.java +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/BannerImage.java @@ -16,12 +16,12 @@ public class BannerImage { private String pcImageUrl; private String mobileImageUrl; - public void changePcImageTo(String changedPcImageUrl) { - this.pcImageUrl = changedPcImageUrl; + public void updatePcImage(String updatePcImageUrl) { + this.pcImageUrl = updatePcImageUrl; } - public void changeMobileImageTo(String changedMobileImageUrl) { - this.pcImageUrl = changedMobileImageUrl; + public void updateMobileImage(String updateMobileImageUrl) { + this.pcImageUrl = updateMobileImageUrl; } } diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishPeriod.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishPeriod.java index c0b9dd2e..eabb9ce6 100644 --- a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishPeriod.java +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishPeriod.java @@ -16,12 +16,12 @@ public class PublishPeriod { private LocalDate startDate; private LocalDate endDate; - public void changeStartDateTo(LocalDate changedDate) { - this.startDate = changedDate; + public void updateStartDate(LocalDate updateDate) { + this.startDate = updateDate; } - public void changeEndDateTo(LocalDate changedDate) { - this.endDate = changedDate; + public void updateEndDate(LocalDate updateDate) { + this.endDate = updateDate; } public PublishStatus getPublishStatus(LocalDate date) { From 94c15e0358880b349091112280bc550eb2c78fcd Mon Sep 17 00:00:00 2001 From: yummygyudon Date: Thu, 12 Dec 2024 04:01:42 +0900 Subject: [PATCH 09/17] =?UTF-8?q?[FEAT]=20Test=20DatabaseCleaner=20?= =?UTF-8?q?=EB=82=B4=20=EC=8B=A0=EA=B7=9C=20=ED=85=8C=EC=9D=B4=EB=B8=94=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 신규 도메인 데이터 테이블로 생성할 Banner (`banners`) 테이블에 대해 복수형 전환 분기 처리를 추가했습니다. * `spring.jpa.properties.hibernate.globally_quoted_identifiers` 속성이 true로 지정될 경우, 자동으로 double quote 가 생성되며 H2 DB에서 테이블 인식에 문제가 생기는 이유로 해당 속성을 제거했습니다. --- .../test/java/org/sopt/makers/operation/DatabaseCleaner.java | 4 ++++ operation-domain/src/test/resources/application-test.yml | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/operation-domain/src/test/java/org/sopt/makers/operation/DatabaseCleaner.java b/operation-domain/src/test/java/org/sopt/makers/operation/DatabaseCleaner.java index dd6bbc2d..7fb8c4a1 100644 --- a/operation-domain/src/test/java/org/sopt/makers/operation/DatabaseCleaner.java +++ b/operation-domain/src/test/java/org/sopt/makers/operation/DatabaseCleaner.java @@ -37,6 +37,9 @@ private void findAllTableNames() { if (snakeName.equals("user")) { return "users"; } + if (snakeName.equals("banner")) { + return "banners"; + } return snakeName; }) .toList(); @@ -58,6 +61,7 @@ private String convertToSnake(String camel) { } index++; } + System.out.println(builder); return builder.toString(); } diff --git a/operation-domain/src/test/resources/application-test.yml b/operation-domain/src/test/resources/application-test.yml index 34fbf3aa..5b0e70b6 100644 --- a/operation-domain/src/test/resources/application-test.yml +++ b/operation-domain/src/test/resources/application-test.yml @@ -6,7 +6,7 @@ spring: username: sa password: driver-class-name: org.h2.Driver - url: jdbc:h2:mem:test;DATABASE_TO_LOWER=true;MODE=PostgreSQL + url: jdbc:h2:mem:test;DATABASE_TO_LOWER=true # ;DATABASE_TO_UPPER=false;MODE=PostgreSQL jpa: hibernate: ddl-auto: update # create-drop 이나 create로 할경우, 자동으로 실행되는 schema & table drop 에서 DDL 오류 발생 (not exist -> not found) @@ -14,7 +14,6 @@ spring: hibernate.dialect: org.hibernate.dialect.PostgreSQLDialect hibernate: format_sql: true - globally_quoted_identifiers: true show_sql: true show-sql: true generate-ddl: true From a2821a9eda2a2ee36a54f8f590be68622b284fba Mon Sep 17 00:00:00 2001 From: yummygyudon Date: Thu, 12 Dec 2024 04:59:05 +0900 Subject: [PATCH 10/17] =?UTF-8?q?[FEAT]=20Banner=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20Entity/Repository=20=EC=A0=95=EC=9D=98=20=EB=B0=8F?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../operation/banner/domain/Banner.java | 68 +++++++++++++++++++ .../banner/repository/BannerRepository.java | 9 +++ 2 files changed, 77 insertions(+) create mode 100644 operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/Banner.java create mode 100644 operation-domain/src/main/java/org/sopt/makers/operation/banner/repository/BannerRepository.java diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/Banner.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/Banner.java new file mode 100644 index 00000000..fc0d2533 --- /dev/null +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/Banner.java @@ -0,0 +1,68 @@ +package org.sopt.makers.operation.banner.domain; + +import jakarta.persistence.*; +import lombok.*; +import org.sopt.makers.operation.common.domain.BaseEntity; + +import java.time.LocalDate; + +import static jakarta.persistence.GenerationType.IDENTITY; +import static lombok.AccessLevel.*; + +@Getter +@Entity +@Table(name = "banners") +@NoArgsConstructor(access = PROTECTED) +@AllArgsConstructor(access = PRIVATE) +public class Banner extends BaseEntity { + + @Id + @GeneratedValue(strategy = IDENTITY) + private Long id; + + @Column(nullable = false) + @Enumerated(EnumType.STRING) + private PublishLocation location; + + @Column(name = "content_type", nullable = false) + @Enumerated(EnumType.STRING) + private ContentType contentType; + + @Column(nullable = false) + private String publisher; + + @Embedded + @AttributeOverrides({ + @AttributeOverride(name = "startDate", column = @Column(name = "start_date", nullable = false)), + @AttributeOverride(name = "endDate", column = @Column(name = "end_date", nullable = false)) + }) + private PublishPeriod period; + + @Embedded + @AttributeOverrides({ + @AttributeOverride(name = "pcImageUrl", column = @Column(name = "img_url_pc", nullable = false)), + @AttributeOverride(name = "mobileImageUrl", column = @Column(name = "img_url_mobile", nullable = false)) + }) + private BannerImage image; + + @Builder + private Banner(PublishLocation location, ContentType contentType, String publisher, PublishPeriod period, BannerImage image) { + this.location = location; + this.contentType = contentType; + this.publisher = publisher; + this.period = period; + this.image = image; + } + + public void updateLocation(PublishLocation location) { + this.location = location; + } + + public void updateContentType(ContentType contentType) { + this.contentType = contentType; + } + + public void updatePublisher(String publisher) { + this.publisher = publisher; + } +} diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/repository/BannerRepository.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/repository/BannerRepository.java new file mode 100644 index 00000000..67a3720e --- /dev/null +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/repository/BannerRepository.java @@ -0,0 +1,9 @@ +package org.sopt.makers.operation.banner.repository; + +import org.sopt.makers.operation.banner.domain.Banner; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface BannerRepository extends JpaRepository { +} From 147bb2750e470cf7afab55fe5e1f1c50177123b9 Mon Sep 17 00:00:00 2001 From: yummygyudon Date: Thu, 12 Dec 2024 05:04:48 +0900 Subject: [PATCH 11/17] =?UTF-8?q?[TEST]=20Banner=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20Entity=20=EB=8B=A8=EC=9C=84=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Embedded 객체 변경이 정상적으로 처리되는지 중점적으로 확인했습니다. * 구현 과정에서 발생했던 오탈자 수정을 진행했습니다.(`BannerImage#updateMobileImage`) --- .../operation/banner/domain/BannerImage.java | 2 +- .../makers/operation/banner/BannerTest.java | 204 ++++++++++++++++++ 2 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 operation-domain/src/test/java/org/sopt/makers/operation/banner/BannerTest.java diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/BannerImage.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/BannerImage.java index 5081e859..be25bef1 100644 --- a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/BannerImage.java +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/BannerImage.java @@ -21,7 +21,7 @@ public void updatePcImage(String updatePcImageUrl) { } public void updateMobileImage(String updateMobileImageUrl) { - this.pcImageUrl = updateMobileImageUrl; + this.mobileImageUrl = updateMobileImageUrl; } } diff --git a/operation-domain/src/test/java/org/sopt/makers/operation/banner/BannerTest.java b/operation-domain/src/test/java/org/sopt/makers/operation/banner/BannerTest.java new file mode 100644 index 00000000..8156bf79 --- /dev/null +++ b/operation-domain/src/test/java/org/sopt/makers/operation/banner/BannerTest.java @@ -0,0 +1,204 @@ +package org.sopt.makers.operation.banner; + +import org.sopt.makers.operation.DatabaseCleaner; + +import org.sopt.makers.operation.banner.domain.Banner; +import org.sopt.makers.operation.banner.domain.ContentType; +import org.sopt.makers.operation.banner.domain.PublishLocation; +import org.sopt.makers.operation.banner.domain.BannerImage; +import org.sopt.makers.operation.banner.domain.PublishPeriod; +import org.sopt.makers.operation.banner.repository.BannerRepository; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +import org.springframework.test.annotation.Rollback; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.TestInstance; + +import java.time.LocalDate; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@DisplayName("[[ Unit Test (Domain) ]] - Banner") +@EntityScan(basePackages = "org.sopt.makers.operation.banner") +@ContextConfiguration(classes = { + Banner.class, BannerImage.class, PublishPeriod.class, + BannerRepository.class, DatabaseCleaner.class +}) +@EnableAutoConfiguration +@ActiveProfiles("test") +public class BannerTest { + private static final long TEST_BANNER_ID = 1L; + private static final PublishLocation TEST_BANNER_LOCATION = PublishLocation.PLAYGROUND_COMMUNITY; + private static final ContentType TEST_BANNER_CONTENT_TYPE = ContentType.PRODUCT; + private static final String TEST_BANNER_PUBLISHER = "PUBLISHER"; + private static final LocalDate TEST_BANNER_START_DATE = LocalDate.of(2024, 1, 1); + private static final LocalDate TEST_BANNER_END_DATE = LocalDate.of(2024, 12, 31); + private static final String TEST_BANNER_PC_IMAGE_URL = "image-url-for-pc"; + private static final String TEST_BANNER_MOBILE_IMAGE_URL = "image-url-for-mobile"; + + private static final PublishPeriod TEST_PUBLISH_PERIOD = new PublishPeriod( + TEST_BANNER_START_DATE, TEST_BANNER_END_DATE + ); + private static final BannerImage TEST_BANNER_IMAGE = new BannerImage( + TEST_BANNER_PC_IMAGE_URL, TEST_BANNER_MOBILE_IMAGE_URL + ); + + private static final Banner TEST_BANNER = Banner.builder() + .location(TEST_BANNER_LOCATION) + .contentType(TEST_BANNER_CONTENT_TYPE) + .publisher(TEST_BANNER_PUBLISHER) + .period(TEST_PUBLISH_PERIOD) + .image(TEST_BANNER_IMAGE) + .build(); + + @Autowired + private BannerRepository bannerRepository; + + @Autowired + private DatabaseCleaner cleaner; + + @Nested + @DisplayName("[TEST] 단일 배너 수정 시나리오") + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + class UpdateTest{ + + @BeforeEach + void initData() { + cleaner.execute(); + bannerRepository.save(TEST_BANNER); + } + + @Test + @DisplayName("게시 기간 중 시작 일자를 정상적으로 변경한다.") + @Rollback(value = false) + void updatePeriodStartDate(){ + // given + Banner givenBanner = bannerRepository.findById(TEST_BANNER_ID).get(); + LocalDate givenUpdateStartDate = LocalDate.of(2024, 6, 30); + + // when + givenBanner.getPeriod().updateStartDate(givenUpdateStartDate); + bannerRepository.save(givenBanner); + + // then + Banner resultBanner = bannerRepository.findById(TEST_BANNER_ID).get(); + assertThat(resultBanner.getPeriod().getStartDate()).isEqualTo(givenUpdateStartDate); + } + + @Test + @DisplayName("게시 기간 중 종료 일자를 정상적으로 변경한다.") + @Rollback(value = false) + void updatePeriodEndDate(){ + // given + Banner givenBanner = bannerRepository.findById(TEST_BANNER_ID).get(); + LocalDate givenUpdateEndDate = LocalDate.of(2030, 12, 31); + + // when + givenBanner.getPeriod().updateEndDate(givenUpdateEndDate); + bannerRepository.save(givenBanner); + + // then + Banner resultBanner = bannerRepository.findById(TEST_BANNER_ID).get(); + assertThat(resultBanner.getPeriod().getEndDate()).isEqualTo(givenUpdateEndDate); + } + + @Test + @DisplayName("게시 이미지 중 PC 사진 URL을 정상적으로 변경한다.") + @Rollback(value = false) + void updateImageForWeb(){ + // given + Banner givenBanner = bannerRepository.findById(TEST_BANNER_ID).get(); + String givenUpdateImageForWeb = "image-url-web-update"; + + // when + givenBanner.getImage().updatePcImage(givenUpdateImageForWeb); + bannerRepository.save(givenBanner); + + // then + Banner resultBanner = bannerRepository.findById(TEST_BANNER_ID).get(); + assertThat(resultBanner.getImage().getPcImageUrl()).isEqualTo(givenUpdateImageForWeb); + } + + @Test + @DisplayName("게시 이미지 중 Mobile 사진 URL을 정상적으로 변경한다.") + @Rollback(value = false) + void updateImageForMobile(){ + // given + Banner givenBanner = bannerRepository.findById(TEST_BANNER_ID).get(); + String givenUpdateImageForMobile = "image-url-mobile-update"; + + // when + givenBanner.getImage().updateMobileImage(givenUpdateImageForMobile); + bannerRepository.save(givenBanner); + + // then + Banner resultBanner = bannerRepository.findById(TEST_BANNER_ID).get(); + assertThat(resultBanner.getImage().getMobileImageUrl()).isEqualTo(givenUpdateImageForMobile); + } + } + + @Nested + @DisplayName("[TEST] 단일 배너 조회 시나리오") + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + class SelectTest{ + + @BeforeAll + void initDatabase() { + cleaner.execute(); + bannerRepository.save(TEST_BANNER); + } + + @Test + @DisplayName("게시 기간을 정상적으로 조회한다.") + void getPeriod() { + // given + Banner givenBanner = bannerRepository.findById(TEST_BANNER_ID).get(); + + // when + PublishPeriod resultPeriod = givenBanner.getPeriod(); + + // then + assertThat(resultPeriod.getStartDate()).isEqualTo(TEST_BANNER_START_DATE); + assertThat(resultPeriod.getEndDate()).isEqualTo(TEST_BANNER_END_DATE); + } + @Test + @DisplayName("게시 이미지들을 정상적으로 조회한다.") + void getImage() { + // given + Banner givenBanner = bannerRepository.findById(TEST_BANNER_ID).get(); + + // when + BannerImage resultImage = givenBanner.getImage(); + + // then + assertThat(resultImage.getPcImageUrl()).isEqualTo(TEST_BANNER_PC_IMAGE_URL); + assertThat(resultImage.getMobileImageUrl()).isEqualTo(TEST_BANNER_MOBILE_IMAGE_URL); + } + + @Test + @DisplayName("게시 위치를 정상적으로 조회한다.") + void getLocation() { + // given + Banner givenBanner = bannerRepository.findById(TEST_BANNER_ID).get(); + + // when + PublishLocation resultLocation = givenBanner.getLocation(); + + // then + assertThat(resultLocation).isEqualTo(TEST_BANNER_LOCATION); + } + } + +} From e1ae1fa87fb8d9c96a3de066ba7fcb483151998d Mon Sep 17 00:00:00 2001 From: yummygyudon Date: Thu, 12 Dec 2024 05:11:49 +0900 Subject: [PATCH 12/17] =?UTF-8?q?[CHORE]=20Unused=20import=20=EB=B0=8F=20?= =?UTF-8?q?=EC=99=80=EC=9D=BC=EB=93=9C=20=EC=B9=B4=EB=93=9C=20import=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/banner/api/BannerApiController.java | 2 +- .../operation/banner/domain/Banner.java | 22 +++++++++++++++---- .../operation/banner/domain/BannerImage.java | 2 +- .../operation/banner/domain/ContentType.java | 2 +- .../banner/domain/PublishLocation.java | 2 +- .../banner/domain/PublishPeriod.java | 6 +++-- .../banner/domain/PublishStatus.java | 2 +- .../banner/repository/BannerRepository.java | 3 ++- 8 files changed, 29 insertions(+), 12 deletions(-) diff --git a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApiController.java b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApiController.java index 606aa7a8..efa2e271 100644 --- a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApiController.java +++ b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApiController.java @@ -2,7 +2,7 @@ import lombok.RequiredArgsConstructor; import lombok.val; -import org.sopt.makers.operation.code.success.web.BannerSuccessCode; + import org.sopt.makers.operation.dto.BaseResponse; import org.sopt.makers.operation.util.ApiResponseUtil; import org.sopt.makers.operation.web.banner.service.BannerService; diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/Banner.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/Banner.java index fc0d2533..82583933 100644 --- a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/Banner.java +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/Banner.java @@ -1,13 +1,27 @@ package org.sopt.makers.operation.banner.domain; -import jakarta.persistence.*; -import lombok.*; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.Entity; +import jakarta.persistence.Column; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Embedded; +import jakarta.persistence.AttributeOverrides; +import jakarta.persistence.AttributeOverride; + +import lombok.Getter; +import lombok.Builder; +import lombok.NoArgsConstructor; +import lombok.AllArgsConstructor; + import org.sopt.makers.operation.common.domain.BaseEntity; -import java.time.LocalDate; import static jakarta.persistence.GenerationType.IDENTITY; -import static lombok.AccessLevel.*; +import static lombok.AccessLevel.PRIVATE; +import static lombok.AccessLevel.PROTECTED; @Getter @Entity diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/BannerImage.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/BannerImage.java index be25bef1..34b6d226 100644 --- a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/BannerImage.java +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/BannerImage.java @@ -1,7 +1,7 @@ package org.sopt.makers.operation.banner.domain; import jakarta.persistence.Embeddable; -import lombok.AccessLevel; + import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/ContentType.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/ContentType.java index 02db9079..852707ac 100644 --- a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/ContentType.java +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/ContentType.java @@ -1,8 +1,8 @@ package org.sopt.makers.operation.banner.domain; -import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; + import org.sopt.makers.operation.code.failure.BannerFailureCode; import org.sopt.makers.operation.exception.BannerException; diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishLocation.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishLocation.java index bab1e832..1c5bc355 100644 --- a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishLocation.java +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishLocation.java @@ -1,8 +1,8 @@ package org.sopt.makers.operation.banner.domain; -import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; + import org.sopt.makers.operation.code.failure.BannerFailureCode; import org.sopt.makers.operation.exception.BannerException; diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishPeriod.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishPeriod.java index eabb9ce6..f2420e9b 100644 --- a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishPeriod.java +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishPeriod.java @@ -1,8 +1,10 @@ package org.sopt.makers.operation.banner.domain; -import jakarta.persistence.Column; import jakarta.persistence.Embeddable; -import lombok.*; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.AllArgsConstructor; import java.time.LocalDate; diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishStatus.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishStatus.java index 5953094a..a6585cf2 100644 --- a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishStatus.java +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishStatus.java @@ -1,8 +1,8 @@ package org.sopt.makers.operation.banner.domain; -import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; + import org.sopt.makers.operation.code.failure.BannerFailureCode; import org.sopt.makers.operation.exception.BannerException; diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/repository/BannerRepository.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/repository/BannerRepository.java index 67a3720e..025c1179 100644 --- a/operation-domain/src/main/java/org/sopt/makers/operation/banner/repository/BannerRepository.java +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/repository/BannerRepository.java @@ -1,8 +1,9 @@ package org.sopt.makers.operation.banner.repository; import org.sopt.makers.operation.banner.domain.Banner; -import org.springframework.data.jpa.repository.JpaRepository; + import org.springframework.stereotype.Repository; +import org.springframework.data.jpa.repository.JpaRepository; @Repository public interface BannerRepository extends JpaRepository { From e76c71dd98be2633f441c3401daea9aad17e7c31 Mon Sep 17 00:00:00 2001 From: yummygyudon Date: Thu, 12 Dec 2024 05:12:58 +0900 Subject: [PATCH 13/17] =?UTF-8?q?[FEAT]=20Entity=20->=20DTO=20=EB=B3=80?= =?UTF-8?q?=ED=99=98=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=A0=95=EC=9D=98=20?= =?UTF-8?q?=EB=B0=8F=20=EB=B0=B0=EB=84=88=20=EC=83=81=EC=84=B8=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20Service=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../banner/dto/response/BannerResponse.java | 16 +++++++++++ .../web/banner/service/BannerServiceImpl.java | 28 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 operation-api/src/main/java/org/sopt/makers/operation/web/banner/service/BannerServiceImpl.java diff --git a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/dto/response/BannerResponse.java b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/dto/response/BannerResponse.java index 6921c7e0..26d554ac 100644 --- a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/dto/response/BannerResponse.java +++ b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/dto/response/BannerResponse.java @@ -1,7 +1,9 @@ package org.sopt.makers.operation.web.banner.dto.response; import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; import lombok.RequiredArgsConstructor; +import org.sopt.makers.operation.banner.domain.Banner; import java.time.LocalDate; @@ -10,6 +12,7 @@ @RequiredArgsConstructor(access = PRIVATE) public final class BannerResponse { + @Builder(access = PRIVATE) public record BannerDetail( @JsonProperty("id") long bannerId, @JsonProperty("status") String bannerStatus, @@ -22,5 +25,18 @@ public record BannerDetail( @JsonProperty("image_url_mobile") String mobileImageUrl ) { + public static BannerDetail fromEntity(Banner banner) { + return BannerDetail.builder() + .bannerId(banner.getId()) + .bannerStatus(banner.getPeriod().getPublishStatus(LocalDate.now()).getValue()) + .bannerLocation(banner.getLocation().getValue()) + .bannerType(banner.getContentType().getValue()) + .publisher(banner.getPublisher()) + .startDate(banner.getPeriod().getStartDate()) + .endDate(banner.getPeriod().getEndDate()) + .pcImageUrl(banner.getImage().getPcImageUrl()) + .mobileImageUrl(banner.getImage().getMobileImageUrl()) + .build(); + } } } diff --git a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/service/BannerServiceImpl.java b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/service/BannerServiceImpl.java new file mode 100644 index 00000000..25a6991c --- /dev/null +++ b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/service/BannerServiceImpl.java @@ -0,0 +1,28 @@ +package org.sopt.makers.operation.web.banner.service; + +import lombok.RequiredArgsConstructor; +import lombok.val; +import org.sopt.makers.operation.banner.domain.Banner; +import org.sopt.makers.operation.banner.repository.BannerRepository; +import org.sopt.makers.operation.code.failure.BannerFailureCode; +import org.sopt.makers.operation.exception.BannerException; +import org.sopt.makers.operation.web.banner.dto.response.BannerResponse; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class BannerServiceImpl implements BannerService { + + private final BannerRepository bannerRepository; + + @Override + public BannerResponse.BannerDetail getBannerDetail(final long bannerId) { + val banner = getBannerById(bannerId); + return BannerResponse.BannerDetail.fromEntity(banner); + } + + private Banner getBannerById(final long id) { + return bannerRepository.findById(id) + .orElseThrow(() -> new BannerException(BannerFailureCode.NOT_FOUNT_BANNER)); + } +} From 3b7680ab500cfb74bec8a9f6902cec3ea06ad602 Mon Sep 17 00:00:00 2001 From: yummygyudon Date: Fri, 13 Dec 2024 00:20:35 +0900 Subject: [PATCH 14/17] =?UTF-8?q?[CHORE]=20`test`=20profile=20Spring=20Boo?= =?UTF-8?q?t=20Test=EB=A5=BC=20=EC=9C=84=ED=95=9C=20Test=20H2=20DB=20Drive?= =?UTF-8?q?r=20Class=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- operation-api/build.gradle | 2 + .../src/main/resources/application-test.yml | 82 +++++++++++++++++++ .../src/test/resources/application-test.yml | 28 ------- 3 files changed, 84 insertions(+), 28 deletions(-) create mode 100644 operation-api/src/main/resources/application-test.yml delete mode 100644 operation-domain/src/test/resources/application-test.yml diff --git a/operation-api/build.gradle b/operation-api/build.gradle index 7af33f94..9a8c28a3 100644 --- a/operation-api/build.gradle +++ b/operation-api/build.gradle @@ -39,4 +39,6 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-api:0.11.2' runtimeOnly "io.jsonwebtoken:jjwt-impl:0.11.2" runtimeOnly "io.jsonwebtoken:jjwt-jackson:0.11.2" + + testImplementation 'com.h2database:h2' } \ No newline at end of file diff --git a/operation-api/src/main/resources/application-test.yml b/operation-api/src/main/resources/application-test.yml new file mode 100644 index 00000000..78b4c645 --- /dev/null +++ b/operation-api/src/main/resources/application-test.yml @@ -0,0 +1,82 @@ +spring: + config: + activate: + on-profile: test + datasource: + username: sa + password: + url: jdbc:h2:mem:test;DATABASE_TO_LOWER=true # ;DATABASE_TO_UPPER=false;MODE=PostgreSQL + jpa: + hibernate: + ddl-auto: update # create-drop 이나 create로 할경우, 자동으로 실행되는 schema & table drop 에서 DDL 오류 발생 (not exist -> not found) + properties: + hibernate.dialect: org.hibernate.dialect.PostgreSQLDialect + hibernate: + format_sql: true + show_sql: true + show-sql: true + generate-ddl: true + jwt: + secretKey: + app: test + access: test + refresh: test + platform_code: test + secretKey: + playground: test + +sopt: + current: + generation: 0 + makers: + playground: + server: test + token: test + alarm: + message: + title_end: test + content_end: test + +admin: + url: + prod: test + dev: test + local: test + +notification: + url: test + key: test + arn: test + +oauth: + apple: + aud: test + sub: test + key: + id: test + path: test + team: + id: test + google: + redirect: + url: test + client: + id: test + secret: test + +cloud: + aws: + credentials: + accessKey: test + secretKey: test + eventBridge: + roleArn: test + +logging: + level: + org: + hibernate: + SQL: DEBUG + type: + descriptor: + sql: trace \ No newline at end of file diff --git a/operation-domain/src/test/resources/application-test.yml b/operation-domain/src/test/resources/application-test.yml deleted file mode 100644 index 5b0e70b6..00000000 --- a/operation-domain/src/test/resources/application-test.yml +++ /dev/null @@ -1,28 +0,0 @@ -spring: - config: - activate: - on-profile: test - datasource: - username: sa - password: - driver-class-name: org.h2.Driver - url: jdbc:h2:mem:test;DATABASE_TO_LOWER=true # ;DATABASE_TO_UPPER=false;MODE=PostgreSQL - jpa: - hibernate: - ddl-auto: update # create-drop 이나 create로 할경우, 자동으로 실행되는 schema & table drop 에서 DDL 오류 발생 (not exist -> not found) - properties: - hibernate.dialect: org.hibernate.dialect.PostgreSQLDialect - hibernate: - format_sql: true - show_sql: true - show-sql: true - generate-ddl: true - -logging: - level: - org: - hibernate: - SQL: DEBUG - type: - descriptor: - sql: trace \ No newline at end of file From 1b86ee3c8f9dcf18a9b8d0f18ec501703b17f370 Mon Sep 17 00:00:00 2001 From: yummygyudon Date: Fri, 13 Dec 2024 00:21:15 +0900 Subject: [PATCH 15/17] [FEAT] Banner API Controller Test - GET `/api/v1/banners/{bannerId}` --- .../banner/api/BannerApiControllerTest.java | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 operation-api/src/test/java/org/sopt/makers/operation/web/banner/api/BannerApiControllerTest.java diff --git a/operation-api/src/test/java/org/sopt/makers/operation/web/banner/api/BannerApiControllerTest.java b/operation-api/src/test/java/org/sopt/makers/operation/web/banner/api/BannerApiControllerTest.java new file mode 100644 index 00000000..1919f6ac --- /dev/null +++ b/operation-api/src/test/java/org/sopt/makers/operation/web/banner/api/BannerApiControllerTest.java @@ -0,0 +1,84 @@ +package org.sopt.makers.operation.web.banner.api; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; + +import org.sopt.makers.operation.code.success.web.BannerSuccessCode; +import org.sopt.makers.operation.filter.JwtAuthenticationFilter; +import org.sopt.makers.operation.filter.JwtExceptionFilter; +import org.sopt.makers.operation.jwt.JwtTokenProvider; +import org.sopt.makers.operation.web.banner.dto.response.BannerResponse; +import org.sopt.makers.operation.web.banner.service.BannerService; + +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.context.annotation.FilterType; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.beans.factory.annotation.Autowired; + +import java.security.Principal; +import java.time.LocalDate; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest( + controllers = {BannerApiController.class}, + excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {JwtAuthenticationFilter.class, JwtExceptionFilter.class})}, + excludeAutoConfiguration = {SecurityAutoConfiguration.class, JwtTokenProvider.class} +) +@DisplayName("[Web Layer Test] Banner API Controller") +class BannerApiControllerTest { + private static final long MOCK_BANNER_ID = 1L; + + @MockBean + private BannerService bannerService; + @Autowired + private MockMvc mockMvc; + + @BeforeEach + void setMockBanner() { + BannerResponse.BannerDetail mockBannerDetail = new BannerResponse.BannerDetail( + MOCK_BANNER_ID, "in_progress", "pg_community", "product", "publisher", + LocalDate.of(2024, 1, 1), LocalDate.of(2024, 12, 31), "image-url-pc", "image-url-mobile" + ); + + when(bannerService.getBannerDetail(MOCK_BANNER_ID)) + .thenReturn(mockBannerDetail); + } + + + @Test + @DisplayName("(GET) Banner Detail") + void getBannerDetail() throws Exception { + // given + BannerResponse.BannerDetail givenBannerDetail = bannerService.getBannerDetail(MOCK_BANNER_ID); + + this.mockMvc.perform( + // when + get("/api/v1/banners/" + MOCK_BANNER_ID) + .contentType(MediaType.APPLICATION_JSON) + .principal(mock(Principal.class))) + // then + .andExpect(status().isOk()) + .andExpect(jsonPath("$.success").value("true")) + .andExpect(jsonPath("$.message").value(BannerSuccessCode.SUCCESS_GET_BANNER_DETAIL.getMessage())) + .andExpect(jsonPath("$.data.id").value(givenBannerDetail.bannerId())) + .andExpect(jsonPath("$.data.status").value(givenBannerDetail.bannerStatus())) + .andExpect(jsonPath("$.data.location").value(givenBannerDetail.bannerLocation())) + .andExpect(jsonPath("$.data.content_type").value(givenBannerDetail.bannerType())) + .andExpect(jsonPath("$.data.publisher").value(givenBannerDetail.publisher())) + .andExpect(jsonPath("$.data.start_date").value(givenBannerDetail.startDate().toString())) + .andExpect(jsonPath("$.data.end_date").value(givenBannerDetail.endDate().toString())) + .andExpect(jsonPath("$.data.image_url_pc").value(givenBannerDetail.pcImageUrl())) + .andExpect(jsonPath("$.data.image_url_mobile").value(givenBannerDetail.mobileImageUrl())); + } +} \ No newline at end of file From 7ae861654a950b6e6a4595e0316bb4f0b29426a3 Mon Sep 17 00:00:00 2001 From: yummygyudon Date: Thu, 19 Dec 2024 16:06:23 +0900 Subject: [PATCH 16/17] =?UTF-8?q?[REFACTOR]=20PublishPeriod=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC=20=EC=83=81=ED=83=9C=20=EC=97=B0=EC=82=B0=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=EB=AC=B8=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - [참고 리뷰](https://github.com/sopt-makers/sopt-operation-backend/pull/288#discussion_r1885484918) --- .../makers/operation/banner/domain/PublishPeriod.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishPeriod.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishPeriod.java index f2420e9b..0d1de579 100644 --- a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishPeriod.java +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishPeriod.java @@ -27,14 +27,14 @@ public void updateEndDate(LocalDate updateDate) { } public PublishStatus getPublishStatus(LocalDate date) { - if (date.isAfter(endDate)) { + boolean isReserved = date.isBefore(startDate); + boolean isDone = date.isAfter(endDate); + if (isDone) { return PublishStatus.DONE; - } else { - if (date.isAfter(startDate)) { - return PublishStatus.IN_PROGRESS; - } + } else if (isReserved){ return PublishStatus.RESERVED; } + return PublishStatus.IN_PROGRESS; } } From bcb8010a304ddeceb546581b1352343b349b6447 Mon Sep 17 00:00:00 2001 From: yummygyudon Date: Thu, 19 Dec 2024 16:12:06 +0900 Subject: [PATCH 17/17] =?UTF-8?q?[STYLE]=20=EC=BD=94=EB=93=9C=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 선언부 분리 개행 - 불필요한 Import 개행 제거 - 불필요한 print 코드 제거 - 설명적 변수 반영 --- .../makers/operation/code/success/web/BannerSuccessCode.java | 3 +-- .../java/org/sopt/makers/operation/banner/domain/Banner.java | 1 - .../sopt/makers/operation/banner/domain/PublishLocation.java | 1 + .../sopt/makers/operation/banner/domain/PublishStatus.java | 1 + .../test/java/org/sopt/makers/operation/DatabaseCleaner.java | 1 - .../java/org/sopt/makers/operation/banner/BannerTest.java | 4 +++- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/operation-common/src/main/java/org/sopt/makers/operation/code/success/web/BannerSuccessCode.java b/operation-common/src/main/java/org/sopt/makers/operation/code/success/web/BannerSuccessCode.java index c5e186b4..618d9f02 100644 --- a/operation-common/src/main/java/org/sopt/makers/operation/code/success/web/BannerSuccessCode.java +++ b/operation-common/src/main/java/org/sopt/makers/operation/code/success/web/BannerSuccessCode.java @@ -1,6 +1,5 @@ package org.sopt.makers.operation.code.success.web; -import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.sopt.makers.operation.code.success.SuccessCode; @@ -12,8 +11,8 @@ @RequiredArgsConstructor(access = PRIVATE) public enum BannerSuccessCode implements SuccessCode { SUCCESS_GET_BANNER_DETAIL(HttpStatus.OK, "배너 상세 정보 조회 성공"), - ; + private final HttpStatus status; private final String message; } diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/Banner.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/Banner.java index 82583933..f0b84e15 100644 --- a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/Banner.java +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/Banner.java @@ -18,7 +18,6 @@ import org.sopt.makers.operation.common.domain.BaseEntity; - import static jakarta.persistence.GenerationType.IDENTITY; import static lombok.AccessLevel.PRIVATE; import static lombok.AccessLevel.PROTECTED; diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishLocation.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishLocation.java index 1c5bc355..c2c19b64 100644 --- a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishLocation.java +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishLocation.java @@ -18,6 +18,7 @@ public enum PublishLocation { CREW_FEED("cr_feed"), OFFICIAL_PAGE("org"), ; + private final String value; public static PublishLocation getByValue(String value) { diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishStatus.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishStatus.java index a6585cf2..87286b6b 100644 --- a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishStatus.java +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/PublishStatus.java @@ -17,6 +17,7 @@ public enum PublishStatus { IN_PROGRESS("in_progress"), DONE("done"), ; + private final String value; public static PublishLocation getByValue(String value) { diff --git a/operation-domain/src/test/java/org/sopt/makers/operation/DatabaseCleaner.java b/operation-domain/src/test/java/org/sopt/makers/operation/DatabaseCleaner.java index 7fb8c4a1..1b824a5d 100644 --- a/operation-domain/src/test/java/org/sopt/makers/operation/DatabaseCleaner.java +++ b/operation-domain/src/test/java/org/sopt/makers/operation/DatabaseCleaner.java @@ -61,7 +61,6 @@ private String convertToSnake(String camel) { } index++; } - System.out.println(builder); return builder.toString(); } diff --git a/operation-domain/src/test/java/org/sopt/makers/operation/banner/BannerTest.java b/operation-domain/src/test/java/org/sopt/makers/operation/banner/BannerTest.java index 8156bf79..f8cdb1c7 100644 --- a/operation-domain/src/test/java/org/sopt/makers/operation/banner/BannerTest.java +++ b/operation-domain/src/test/java/org/sopt/makers/operation/banner/BannerTest.java @@ -94,7 +94,8 @@ void updatePeriodStartDate(){ // then Banner resultBanner = bannerRepository.findById(TEST_BANNER_ID).get(); - assertThat(resultBanner.getPeriod().getStartDate()).isEqualTo(givenUpdateStartDate); + LocalDate resultBannerStartDate = resultBanner.getPeriod().getStartDate(); + assertThat(resultBannerStartDate).isEqualTo(givenUpdateStartDate); } @Test @@ -173,6 +174,7 @@ void getPeriod() { assertThat(resultPeriod.getStartDate()).isEqualTo(TEST_BANNER_START_DATE); assertThat(resultPeriod.getEndDate()).isEqualTo(TEST_BANNER_END_DATE); } + @Test @DisplayName("게시 이미지들을 정상적으로 조회한다.") void getImage() {