From ff9d43e66db38745eae06f0617d2560e9426079a Mon Sep 17 00:00:00 2001 From: "lalla.you" Date: Sun, 13 Apr 2025 20:04:50 +0900 Subject: [PATCH 1/8] =?UTF-8?q?docs:=202=EB=8B=A8=EA=B3=84=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=EC=82=AC=ED=95=AD=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4ba6c8e72..d0d817c98 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,24 @@ ## 온라인 코드 리뷰 과정 * [텍스트와 이미지로 살펴보는 온라인 코드 리뷰 과정](https://github.com/next-step/nextstep-docs/tree/master/codereview) -## 요구사항 +## 기능 요구사항 +### 1단계 - 레거시 코드 리팩토링 - deleteQuestion 에서 비즈니스 로직을 도메인 모델로 리팩토링 - [X] 로그인 사용자와 질문한 사람이 같은 경우 삭제 가능 - [X] 질문자와 답변글의 모든 답변자가 같은 경우 삭제 가능 - [X] 삭제 이력 남기기 +### 2단계 - 수강신청(도메인 모델) +- [ ] 과정(Course)은 기수 단위로 운영, 여러개의 강의(Session)존재 +- [ ] 강의는 시작일과 종료일이 존재 +- [ ] 강의는 강의 커버 이미지가 존재 + - [ ] 이미지 크기는 1MB 이하 + - [ ] 이미지 확장자는 gif, jpg, jpeg, png, svg 만 가능 + - [ ] width는 300픽셀, height는 200픽셀 이상이, width와 height의 비율은 3:2 +- [ ] 강의는 무료강의와 유료강의 분류 + - [ ] 무료강의 - 최대 수강 인원 제한 X + - [ ] 유료강의 - 최대 수강 인원 제한 O + - [ ] 유료강의 - 수강생이 결제한 금액과 수강료가 일치할 때 수강 신청이 가능 +- [ ] 강의 상태 : 준비중, 모집중, 종료 +- [ ] 강의 상태가 모집중일 때만 수강신청 가능 +- [ ] 결제 정보는 payments 모듈을 통해 관리, 결제 정보는 Payment 객체 From 4795bb887e2a8517f4872a666148b2f543347e84 Mon Sep 17 00:00:00 2001 From: "lalla.you" Date: Sun, 13 Apr 2025 21:14:08 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20CoverImage=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +-- .../courses/InvalidCoverImageException.java | 8 +++ .../nextstep/courses/domain/CoverImage.java | 38 ++++++++++++++ .../courses/domain/ImageExtension.java | 16 ++++++ .../courses/domain/CoverImageTest.java | 52 +++++++++++++++++++ 5 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 src/main/java/nextstep/courses/InvalidCoverImageException.java create mode 100644 src/main/java/nextstep/courses/domain/CoverImage.java create mode 100644 src/main/java/nextstep/courses/domain/ImageExtension.java create mode 100644 src/test/java/nextstep/courses/domain/CoverImageTest.java diff --git a/README.md b/README.md index d0d817c98..359ffd596 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,9 @@ - [ ] 과정(Course)은 기수 단위로 운영, 여러개의 강의(Session)존재 - [ ] 강의는 시작일과 종료일이 존재 - [ ] 강의는 강의 커버 이미지가 존재 - - [ ] 이미지 크기는 1MB 이하 - - [ ] 이미지 확장자는 gif, jpg, jpeg, png, svg 만 가능 - - [ ] width는 300픽셀, height는 200픽셀 이상이, width와 height의 비율은 3:2 + - [X] 이미지 크기는 1MB 이하 + - [X] 이미지 확장자는 gif, jpg, jpeg, png, svg 만 가능 + - [X] width는 300픽셀, height는 200픽셀 이상이, width와 height의 비율은 3:2 - [ ] 강의는 무료강의와 유료강의 분류 - [ ] 무료강의 - 최대 수강 인원 제한 X - [ ] 유료강의 - 최대 수강 인원 제한 O diff --git a/src/main/java/nextstep/courses/InvalidCoverImageException.java b/src/main/java/nextstep/courses/InvalidCoverImageException.java new file mode 100644 index 000000000..cb8d060e7 --- /dev/null +++ b/src/main/java/nextstep/courses/InvalidCoverImageException.java @@ -0,0 +1,8 @@ +package nextstep.courses; + +public class InvalidCoverImageException extends RuntimeException { + + public InvalidCoverImageException(String message) { + super(message); + } +} diff --git a/src/main/java/nextstep/courses/domain/CoverImage.java b/src/main/java/nextstep/courses/domain/CoverImage.java new file mode 100644 index 000000000..03251a011 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/CoverImage.java @@ -0,0 +1,38 @@ +package nextstep.courses.domain; + +import nextstep.courses.InvalidCoverImageException; + +public class CoverImage { + + private final int width; + private final int height; + private final int sizeInBytes; + private final ImageExtension extension; + + public CoverImage(int width, int height, int sizeInBytes, ImageExtension extension) { + this.width = width; + this.height = height; + this.sizeInBytes = sizeInBytes; + this.extension = extension; + validate(); + } + + private void validate() { + if (sizeInBytes > 1_000_000) { + throw new InvalidCoverImageException("1MB를 초과하는 이미지입니다."); + } + if (width < 300) { + throw new InvalidCoverImageException("너비는 300px 이상이어야 합니다."); + } + if (height < 200) { + throw new InvalidCoverImageException("높이는 200px 이상이어야 합니다."); + } + if (width * 2 != height * 3) { + throw new InvalidCoverImageException("비율은 3:2여야 합니다."); + } + if (extension == null) { + throw new InvalidCoverImageException("지원하지 않는 확장자입니다."); + } + } + +} diff --git a/src/main/java/nextstep/courses/domain/ImageExtension.java b/src/main/java/nextstep/courses/domain/ImageExtension.java new file mode 100644 index 000000000..98c4817f7 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/ImageExtension.java @@ -0,0 +1,16 @@ +package nextstep.courses.domain; + +public enum ImageExtension { + GIF("gif"), + JPG("jpg"), + JPEG("jpeg"), + PNG("png"), + SVG("svg"); + + private final String value; + + ImageExtension(String value) { + this.value = value; + } + +} diff --git a/src/test/java/nextstep/courses/domain/CoverImageTest.java b/src/test/java/nextstep/courses/domain/CoverImageTest.java new file mode 100644 index 000000000..9d1f9f2ed --- /dev/null +++ b/src/test/java/nextstep/courses/domain/CoverImageTest.java @@ -0,0 +1,52 @@ +package nextstep.courses.domain; + +import nextstep.courses.InvalidCoverImageException; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +public class CoverImageTest { + + @Test + void 유효한_커버이미지는_예외없이_생성되고_검증_통과() { + assertThatCode(() -> + new CoverImage(600, 400, 500_000, ImageExtension.JPG) + ).doesNotThrowAnyException(); + } + + @Test + void 크기가_1MB_초과하면_예외발생() { + assertThatThrownBy(() -> + new CoverImage(600, 400, 2_000_000, ImageExtension.PNG) + ).isInstanceOf(InvalidCoverImageException.class); + } + + @Test + void 너비가_300미만이면_예외발생() { + assertThatThrownBy(() -> + new CoverImage(299, 400, 500_000, ImageExtension.PNG) + ).isInstanceOf(InvalidCoverImageException.class); + } + + @Test + void 높이가_200미만이면_예외발생() { + assertThatThrownBy(() -> + new CoverImage(600, 199, 500_000, ImageExtension.PNG) + ).isInstanceOf(InvalidCoverImageException.class); + } + + @Test + void 비율이_3대2_아니면_예외발생() { + assertThatThrownBy(() -> + new CoverImage(600, 500, 500_000, ImageExtension.PNG) + ).isInstanceOf(InvalidCoverImageException.class); + } + + @Test + void 지원하지_않는_확장자면_예외발생() { + assertThatThrownBy(() -> + new CoverImage(600, 400, 500_000, null) + ).isInstanceOf(InvalidCoverImageException.class); + } +} From 568fda885e11ee167839bb74851f508b39eb1fcf Mon Sep 17 00:00:00 2001 From: "lalla.you" Date: Sun, 13 Apr 2025 21:45:34 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20=EB=AC=B4=EB=A3=8C=EA=B0=95?= =?UTF-8?q?=EC=9D=98,=EC=9C=A0=EB=A3=8C=EA=B0=95=EC=9D=98=20=EB=B6=84?= =?UTF-8?q?=EB=A5=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++-- .../payments/domain/EnrollmentPolicy.java | 5 +++ .../payments/domain/FreeEnrollmentPolicy.java | 9 +++++ .../payments/domain/PaidEnrollmentPolicy.java | 18 +++++++++ .../payments/EnrollmentPolicyTest.java | 38 +++++++++++++++++++ 5 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 src/main/java/nextstep/payments/domain/EnrollmentPolicy.java create mode 100644 src/main/java/nextstep/payments/domain/FreeEnrollmentPolicy.java create mode 100644 src/main/java/nextstep/payments/domain/PaidEnrollmentPolicy.java create mode 100644 src/test/java/nextstep/payments/EnrollmentPolicyTest.java diff --git a/README.md b/README.md index 359ffd596..52fa01639 100644 --- a/README.md +++ b/README.md @@ -22,10 +22,10 @@ - [X] 이미지 크기는 1MB 이하 - [X] 이미지 확장자는 gif, jpg, jpeg, png, svg 만 가능 - [X] width는 300픽셀, height는 200픽셀 이상이, width와 height의 비율은 3:2 -- [ ] 강의는 무료강의와 유료강의 분류 - - [ ] 무료강의 - 최대 수강 인원 제한 X - - [ ] 유료강의 - 최대 수강 인원 제한 O - - [ ] 유료강의 - 수강생이 결제한 금액과 수강료가 일치할 때 수강 신청이 가능 +- [X] 강의는 무료강의와 유료강의 분류 + - [X] 무료강의 - 최대 수강 인원 제한 X + - [X] 유료강의 - 최대 수강 인원 제한 O + - [X] 유료강의 - 수강생이 결제한 금액과 수강료가 일치할 때 수강 신청이 가능 - [ ] 강의 상태 : 준비중, 모집중, 종료 - [ ] 강의 상태가 모집중일 때만 수강신청 가능 - [ ] 결제 정보는 payments 모듈을 통해 관리, 결제 정보는 Payment 객체 diff --git a/src/main/java/nextstep/payments/domain/EnrollmentPolicy.java b/src/main/java/nextstep/payments/domain/EnrollmentPolicy.java new file mode 100644 index 000000000..c46b8a14a --- /dev/null +++ b/src/main/java/nextstep/payments/domain/EnrollmentPolicy.java @@ -0,0 +1,5 @@ +package nextstep.payments.domain; + +public interface EnrollmentPolicy { + boolean canEnroll(int currentEnrolledCount, int paidAmount); +} diff --git a/src/main/java/nextstep/payments/domain/FreeEnrollmentPolicy.java b/src/main/java/nextstep/payments/domain/FreeEnrollmentPolicy.java new file mode 100644 index 000000000..973384ece --- /dev/null +++ b/src/main/java/nextstep/payments/domain/FreeEnrollmentPolicy.java @@ -0,0 +1,9 @@ +package nextstep.payments.domain; + +public class FreeEnrollmentPolicy implements EnrollmentPolicy { + + @Override + public boolean canEnroll(int currentEnrolledCount, int paidAmount) { + return paidAmount == 0; + } +} diff --git a/src/main/java/nextstep/payments/domain/PaidEnrollmentPolicy.java b/src/main/java/nextstep/payments/domain/PaidEnrollmentPolicy.java new file mode 100644 index 000000000..9d7166ec2 --- /dev/null +++ b/src/main/java/nextstep/payments/domain/PaidEnrollmentPolicy.java @@ -0,0 +1,18 @@ +package nextstep.payments.domain; + +public class PaidEnrollmentPolicy implements EnrollmentPolicy { + + private final int capacity; + private final int price; + + + public PaidEnrollmentPolicy(int capacity, int price) { + this.capacity = capacity; + this.price = price; + } + + @Override + public boolean canEnroll(int currentEnrolledCount, int paidAmount) { + return currentEnrolledCount < capacity && paidAmount == price; + } +} diff --git a/src/test/java/nextstep/payments/EnrollmentPolicyTest.java b/src/test/java/nextstep/payments/EnrollmentPolicyTest.java new file mode 100644 index 000000000..b84004084 --- /dev/null +++ b/src/test/java/nextstep/payments/EnrollmentPolicyTest.java @@ -0,0 +1,38 @@ +package nextstep.payments; + +import nextstep.payments.domain.EnrollmentPolicy; +import nextstep.payments.domain.FreeEnrollmentPolicy; +import nextstep.payments.domain.PaidEnrollmentPolicy; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class EnrollmentPolicyTest { + + @Test + void 무료_강의_최대_수강_인원_제한이_없다() { + EnrollmentPolicy enrollmentPolicy = new FreeEnrollmentPolicy(); + assertTrue(enrollmentPolicy.canEnroll(9999, 0)); + } + + @Test + void 유료_강의_최대_수강_인원_제한이_있다() { + EnrollmentPolicy enrollmentPolicy = new PaidEnrollmentPolicy(10, 5000); + assertTrue(enrollmentPolicy.canEnroll(3, 5000)); + assertFalse(enrollmentPolicy.canEnroll(10, 5000)); + } + + @Test + void 유료_강의_수강료가_같으면_수강할_수_있다() { + EnrollmentPolicy enrollmentPolicy = new PaidEnrollmentPolicy(10, 5000); + assertFalse(enrollmentPolicy.canEnroll(3, 5000)); + } + + @Test + void 유료_강의_수강료가_다르면_수강할_수_없다() { + EnrollmentPolicy enrollmentPolicy = new PaidEnrollmentPolicy(10, 5000); + assertFalse(enrollmentPolicy.canEnroll(3, 10000)); + } + +} From 35e53a3d94bbfebc7668dc8e382da20d47235220 Mon Sep 17 00:00:00 2001 From: "lalla.you" Date: Sun, 13 Apr 2025 21:45:53 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20=EA=B0=95=EC=9D=98=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/main/java/nextstep/courses/domain/SessionStatus.java | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 src/main/java/nextstep/courses/domain/SessionStatus.java diff --git a/README.md b/README.md index 52fa01639..593212f3f 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,6 @@ - [X] 무료강의 - 최대 수강 인원 제한 X - [X] 유료강의 - 최대 수강 인원 제한 O - [X] 유료강의 - 수강생이 결제한 금액과 수강료가 일치할 때 수강 신청이 가능 -- [ ] 강의 상태 : 준비중, 모집중, 종료 +- [X] 강의 상태 : 준비중, 모집중, 종료 - [ ] 강의 상태가 모집중일 때만 수강신청 가능 - [ ] 결제 정보는 payments 모듈을 통해 관리, 결제 정보는 Payment 객체 diff --git a/src/main/java/nextstep/courses/domain/SessionStatus.java b/src/main/java/nextstep/courses/domain/SessionStatus.java new file mode 100644 index 000000000..e8d4921d3 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/SessionStatus.java @@ -0,0 +1,7 @@ +package nextstep.courses.domain; + +public enum SessionStatus { + READY, + ENROLLING, + COMPLETED +} From 0bc40f527051ef165c81aa2bb019b017826df7df Mon Sep 17 00:00:00 2001 From: "lalla.you" Date: Sun, 13 Apr 2025 22:04:00 +0900 Subject: [PATCH 5/8] =?UTF-8?q?feat:=20Session=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=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 --- README.md | 6 +-- .../courses/CannotEnrollSessionException.java | 7 ++++ .../java/nextstep/courses/domain/Session.java | 42 +++++++++++++++++++ .../courses/domain/SessionStatus.java | 6 ++- .../nextstep/courses/domain/SessionTest.java | 41 ++++++++++++++++++ .../payments/EnrollmentPolicyTest.java | 2 +- 6 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 src/main/java/nextstep/courses/CannotEnrollSessionException.java create mode 100644 src/main/java/nextstep/courses/domain/Session.java create mode 100644 src/test/java/nextstep/courses/domain/SessionTest.java diff --git a/README.md b/README.md index 593212f3f..541bbbfd2 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ ### 2단계 - 수강신청(도메인 모델) - [ ] 과정(Course)은 기수 단위로 운영, 여러개의 강의(Session)존재 -- [ ] 강의는 시작일과 종료일이 존재 -- [ ] 강의는 강의 커버 이미지가 존재 +- [X] 강의는 시작일과 종료일이 존재 +- [X] 강의는 강의 커버 이미지가 존재 - [X] 이미지 크기는 1MB 이하 - [X] 이미지 확장자는 gif, jpg, jpeg, png, svg 만 가능 - [X] width는 300픽셀, height는 200픽셀 이상이, width와 height의 비율은 3:2 @@ -27,5 +27,5 @@ - [X] 유료강의 - 최대 수강 인원 제한 O - [X] 유료강의 - 수강생이 결제한 금액과 수강료가 일치할 때 수강 신청이 가능 - [X] 강의 상태 : 준비중, 모집중, 종료 -- [ ] 강의 상태가 모집중일 때만 수강신청 가능 +- [X] 강의 상태가 모집중일 때만 수강신청 가능 - [ ] 결제 정보는 payments 모듈을 통해 관리, 결제 정보는 Payment 객체 diff --git a/src/main/java/nextstep/courses/CannotEnrollSessionException.java b/src/main/java/nextstep/courses/CannotEnrollSessionException.java new file mode 100644 index 000000000..819368ef9 --- /dev/null +++ b/src/main/java/nextstep/courses/CannotEnrollSessionException.java @@ -0,0 +1,7 @@ +package nextstep.courses; + +public class CannotEnrollSessionException extends RuntimeException { + public CannotEnrollSessionException(String message) { + super(message); + } +} diff --git a/src/main/java/nextstep/courses/domain/Session.java b/src/main/java/nextstep/courses/domain/Session.java new file mode 100644 index 000000000..7e1e7f7b3 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/Session.java @@ -0,0 +1,42 @@ +package nextstep.courses.domain; + +import nextstep.courses.CannotEnrollSessionException; +import nextstep.payments.domain.EnrollmentPolicy; + +import java.time.LocalDate; + +public class Session { + + private final LocalDate startDate; + private final LocalDate endDate; + private final CoverImage coverImage; + private final SessionStatus status; + private final EnrollmentPolicy enrollmentPolicy; + + private int currentEnrolledCount = 0; + + public Session(LocalDate startDate, + LocalDate endDate, + CoverImage coverImage, + SessionStatus status, + EnrollmentPolicy enrollmentPolicy) { + this.startDate = startDate; + this.endDate = endDate; + this.coverImage = coverImage; + this.status = status; + this.enrollmentPolicy = enrollmentPolicy; + } + + public void enroll(int paidAmount) { + if (!this.status.canEnroll()) { + throw new CannotEnrollSessionException("모집 중이 아닙니다."); + } + + if (!enrollmentPolicy.canEnroll(currentEnrolledCount, paidAmount)) { + throw new CannotEnrollSessionException("수강 조건이 맞지 않습니다."); + } + + currentEnrolledCount++; + } + +} diff --git a/src/main/java/nextstep/courses/domain/SessionStatus.java b/src/main/java/nextstep/courses/domain/SessionStatus.java index e8d4921d3..19b3a74b6 100644 --- a/src/main/java/nextstep/courses/domain/SessionStatus.java +++ b/src/main/java/nextstep/courses/domain/SessionStatus.java @@ -3,5 +3,9 @@ public enum SessionStatus { READY, ENROLLING, - COMPLETED + COMPLETED; + + public boolean canEnroll() { + return this == ENROLLING; + } } diff --git a/src/test/java/nextstep/courses/domain/SessionTest.java b/src/test/java/nextstep/courses/domain/SessionTest.java new file mode 100644 index 000000000..8105d5123 --- /dev/null +++ b/src/test/java/nextstep/courses/domain/SessionTest.java @@ -0,0 +1,41 @@ +package nextstep.courses.domain; + +import nextstep.courses.CannotEnrollSessionException; +import nextstep.payments.domain.PaidEnrollmentPolicy; +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +public class SessionTest { + + public static final Session ReadySession = new Session( + LocalDate.of(2025, 1, 1), + LocalDate.of(2025, 12, 31), + new CoverImage(600, 400, 500_000, ImageExtension.JPG), + SessionStatus.READY, + new PaidEnrollmentPolicy(100, 1000)); + + public static final Session EnrollingSession = new Session( + LocalDate.of(2025, 1, 1), + LocalDate.of(2025, 12, 31), + new CoverImage(600, 400, 500_000, ImageExtension.JPG), + SessionStatus.ENROLLING, + new PaidEnrollmentPolicy(100, 1000)); + + @Test + void 모집중이_아닐_때_수강신청_불가능() { + assertThatThrownBy(() -> + ReadySession.enroll(1000) + ).isInstanceOf(CannotEnrollSessionException.class); + } + + @Test + void 모집중일_때_수강신청_가능() { + assertThatCode(() -> EnrollingSession.enroll(1000)) + .doesNotThrowAnyException(); + + } +} diff --git a/src/test/java/nextstep/payments/EnrollmentPolicyTest.java b/src/test/java/nextstep/payments/EnrollmentPolicyTest.java index b84004084..dead7a065 100644 --- a/src/test/java/nextstep/payments/EnrollmentPolicyTest.java +++ b/src/test/java/nextstep/payments/EnrollmentPolicyTest.java @@ -26,7 +26,7 @@ public class EnrollmentPolicyTest { @Test void 유료_강의_수강료가_같으면_수강할_수_있다() { EnrollmentPolicy enrollmentPolicy = new PaidEnrollmentPolicy(10, 5000); - assertFalse(enrollmentPolicy.canEnroll(3, 5000)); + assertTrue(enrollmentPolicy.canEnroll(3, 5000)); } @Test From e91212d4504932b4e5c670dc8f96069cfa2b848f Mon Sep 17 00:00:00 2001 From: "lalla.you" Date: Sun, 13 Apr 2025 22:16:15 +0900 Subject: [PATCH 6/8] =?UTF-8?q?feat:=20=EA=B2=B0=EC=A0=9C=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=EB=8A=94=20payment=20=EB=A1=9C=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../java/nextstep/courses/domain/Session.java | 5 ++-- .../payments/domain/EnrollmentPolicy.java | 2 +- .../payments/domain/FreeEnrollmentPolicy.java | 4 +-- .../payments/domain/PaidEnrollmentPolicy.java | 4 +-- .../nextstep/payments/domain/Payment.java | 4 +++ .../nextstep/courses/domain/SessionTest.java | 5 ++-- .../payments/EnrollmentPolicyTest.java | 16 ++++++----- .../java/nextstep/payments/PaymentTest.java | 28 +++++++++++++++++++ 9 files changed, 53 insertions(+), 17 deletions(-) create mode 100644 src/test/java/nextstep/payments/PaymentTest.java diff --git a/README.md b/README.md index 541bbbfd2..144246cdc 100644 --- a/README.md +++ b/README.md @@ -28,4 +28,4 @@ - [X] 유료강의 - 수강생이 결제한 금액과 수강료가 일치할 때 수강 신청이 가능 - [X] 강의 상태 : 준비중, 모집중, 종료 - [X] 강의 상태가 모집중일 때만 수강신청 가능 -- [ ] 결제 정보는 payments 모듈을 통해 관리, 결제 정보는 Payment 객체 +- [X] 결제 정보는 payments 모듈을 통해 관리, 결제 정보는 Payment 객체 diff --git a/src/main/java/nextstep/courses/domain/Session.java b/src/main/java/nextstep/courses/domain/Session.java index 7e1e7f7b3..c23638653 100644 --- a/src/main/java/nextstep/courses/domain/Session.java +++ b/src/main/java/nextstep/courses/domain/Session.java @@ -2,6 +2,7 @@ import nextstep.courses.CannotEnrollSessionException; import nextstep.payments.domain.EnrollmentPolicy; +import nextstep.payments.domain.Payment; import java.time.LocalDate; @@ -27,12 +28,12 @@ public Session(LocalDate startDate, this.enrollmentPolicy = enrollmentPolicy; } - public void enroll(int paidAmount) { + public void enroll(Payment payment) { if (!this.status.canEnroll()) { throw new CannotEnrollSessionException("모집 중이 아닙니다."); } - if (!enrollmentPolicy.canEnroll(currentEnrolledCount, paidAmount)) { + if (!enrollmentPolicy.canEnroll(currentEnrolledCount, payment)) { throw new CannotEnrollSessionException("수강 조건이 맞지 않습니다."); } diff --git a/src/main/java/nextstep/payments/domain/EnrollmentPolicy.java b/src/main/java/nextstep/payments/domain/EnrollmentPolicy.java index c46b8a14a..e1845f7ae 100644 --- a/src/main/java/nextstep/payments/domain/EnrollmentPolicy.java +++ b/src/main/java/nextstep/payments/domain/EnrollmentPolicy.java @@ -1,5 +1,5 @@ package nextstep.payments.domain; public interface EnrollmentPolicy { - boolean canEnroll(int currentEnrolledCount, int paidAmount); + boolean canEnroll(int currentEnrolledCount, Payment payment); } diff --git a/src/main/java/nextstep/payments/domain/FreeEnrollmentPolicy.java b/src/main/java/nextstep/payments/domain/FreeEnrollmentPolicy.java index 973384ece..92145f74d 100644 --- a/src/main/java/nextstep/payments/domain/FreeEnrollmentPolicy.java +++ b/src/main/java/nextstep/payments/domain/FreeEnrollmentPolicy.java @@ -3,7 +3,7 @@ public class FreeEnrollmentPolicy implements EnrollmentPolicy { @Override - public boolean canEnroll(int currentEnrolledCount, int paidAmount) { - return paidAmount == 0; + public boolean canEnroll(int currentEnrolledCount, Payment payment) { + return payment.isSameAmount(0L); } } diff --git a/src/main/java/nextstep/payments/domain/PaidEnrollmentPolicy.java b/src/main/java/nextstep/payments/domain/PaidEnrollmentPolicy.java index 9d7166ec2..45982a8dc 100644 --- a/src/main/java/nextstep/payments/domain/PaidEnrollmentPolicy.java +++ b/src/main/java/nextstep/payments/domain/PaidEnrollmentPolicy.java @@ -12,7 +12,7 @@ public PaidEnrollmentPolicy(int capacity, int price) { } @Override - public boolean canEnroll(int currentEnrolledCount, int paidAmount) { - return currentEnrolledCount < capacity && paidAmount == price; + public boolean canEnroll(int currentEnrolledCount, Payment payment) { + return currentEnrolledCount < capacity && payment.isSameAmount((long) price); } } diff --git a/src/main/java/nextstep/payments/domain/Payment.java b/src/main/java/nextstep/payments/domain/Payment.java index 57d833f85..ef093c387 100644 --- a/src/main/java/nextstep/payments/domain/Payment.java +++ b/src/main/java/nextstep/payments/domain/Payment.java @@ -26,4 +26,8 @@ public Payment(String id, Long sessionId, Long nsUserId, Long amount) { this.amount = amount; this.createdAt = LocalDateTime.now(); } + + public boolean isSameAmount(Long amount) { + return this.amount.equals(amount); + } } diff --git a/src/test/java/nextstep/courses/domain/SessionTest.java b/src/test/java/nextstep/courses/domain/SessionTest.java index 8105d5123..954cc4d4a 100644 --- a/src/test/java/nextstep/courses/domain/SessionTest.java +++ b/src/test/java/nextstep/courses/domain/SessionTest.java @@ -6,6 +6,7 @@ import java.time.LocalDate; +import static nextstep.payments.PaymentTest.PAYMENT_1000; import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; @@ -28,13 +29,13 @@ public class SessionTest { @Test void 모집중이_아닐_때_수강신청_불가능() { assertThatThrownBy(() -> - ReadySession.enroll(1000) + ReadySession.enroll(PAYMENT_1000) ).isInstanceOf(CannotEnrollSessionException.class); } @Test void 모집중일_때_수강신청_가능() { - assertThatCode(() -> EnrollingSession.enroll(1000)) + assertThatCode(() -> EnrollingSession.enroll(PAYMENT_1000)) .doesNotThrowAnyException(); } diff --git a/src/test/java/nextstep/payments/EnrollmentPolicyTest.java b/src/test/java/nextstep/payments/EnrollmentPolicyTest.java index dead7a065..62648f054 100644 --- a/src/test/java/nextstep/payments/EnrollmentPolicyTest.java +++ b/src/test/java/nextstep/payments/EnrollmentPolicyTest.java @@ -5,6 +5,8 @@ import nextstep.payments.domain.PaidEnrollmentPolicy; import org.junit.jupiter.api.Test; +import static nextstep.payments.PaymentTest.PAYMENT_1000; +import static nextstep.payments.PaymentTest.PAYMENT_FREE; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -13,26 +15,26 @@ public class EnrollmentPolicyTest { @Test void 무료_강의_최대_수강_인원_제한이_없다() { EnrollmentPolicy enrollmentPolicy = new FreeEnrollmentPolicy(); - assertTrue(enrollmentPolicy.canEnroll(9999, 0)); + assertTrue(enrollmentPolicy.canEnroll(9999, PAYMENT_FREE)); } @Test void 유료_강의_최대_수강_인원_제한이_있다() { - EnrollmentPolicy enrollmentPolicy = new PaidEnrollmentPolicy(10, 5000); - assertTrue(enrollmentPolicy.canEnroll(3, 5000)); - assertFalse(enrollmentPolicy.canEnroll(10, 5000)); + EnrollmentPolicy enrollmentPolicy = new PaidEnrollmentPolicy(10, 1000); + assertTrue(enrollmentPolicy.canEnroll(3, PAYMENT_1000)); + assertFalse(enrollmentPolicy.canEnroll(10, PAYMENT_1000)); } @Test void 유료_강의_수강료가_같으면_수강할_수_있다() { - EnrollmentPolicy enrollmentPolicy = new PaidEnrollmentPolicy(10, 5000); - assertTrue(enrollmentPolicy.canEnroll(3, 5000)); + EnrollmentPolicy enrollmentPolicy = new PaidEnrollmentPolicy(10, 1000); + assertTrue(enrollmentPolicy.canEnroll(3, PAYMENT_1000)); } @Test void 유료_강의_수강료가_다르면_수강할_수_없다() { EnrollmentPolicy enrollmentPolicy = new PaidEnrollmentPolicy(10, 5000); - assertFalse(enrollmentPolicy.canEnroll(3, 10000)); + assertFalse(enrollmentPolicy.canEnroll(3, PAYMENT_1000)); } } diff --git a/src/test/java/nextstep/payments/PaymentTest.java b/src/test/java/nextstep/payments/PaymentTest.java new file mode 100644 index 000000000..b326aaea5 --- /dev/null +++ b/src/test/java/nextstep/payments/PaymentTest.java @@ -0,0 +1,28 @@ +package nextstep.payments; + +import nextstep.payments.domain.Payment; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class PaymentTest { + + public static final Payment PAYMENT_FREE = new Payment( + "12345", 1L, 100L, 0L + ); + + public static final Payment PAYMENT_1000 = new Payment( + "12345", 1L, 100L, 1000L + ); + + @Test + void 결제금액이_같으면_true를_반환한다() { + assertTrue(PAYMENT_1000.isSameAmount(1000L)); + } + + @Test + void 결제금액이_다르면_false를_반환한다() { + assertFalse(PAYMENT_1000.isSameAmount(2000L)); + } +} From a3c196178a79cb31e21858d4b5566924416f1155 Mon Sep 17 00:00:00 2001 From: "lalla.you" Date: Mon, 14 Apr 2025 00:21:01 +0900 Subject: [PATCH 7/8] =?UTF-8?q?feat:=20=EA=B8=B0=EC=88=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80,=20Sessions=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../java/nextstep/courses/domain/Course.java | 12 +++++++++++- .../java/nextstep/courses/domain/Sessions.java | 16 ++++++++++++++++ .../infrastructure/JdbcCourseRepository.java | 1 + .../payments/domain/PaidEnrollmentPolicy.java | 8 ++++---- 5 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 src/main/java/nextstep/courses/domain/Sessions.java diff --git a/README.md b/README.md index 144246cdc..3fa8e65b3 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ - [X] 삭제 이력 남기기 ### 2단계 - 수강신청(도메인 모델) -- [ ] 과정(Course)은 기수 단위로 운영, 여러개의 강의(Session)존재 +- [X] 과정(Course)은 기수 단위로 운영, 여러개의 강의(Session)존재 - [X] 강의는 시작일과 종료일이 존재 - [X] 강의는 강의 커버 이미지가 존재 - [X] 이미지 크기는 1MB 이하 diff --git a/src/main/java/nextstep/courses/domain/Course.java b/src/main/java/nextstep/courses/domain/Course.java index 0f6971604..ad47f3d28 100644 --- a/src/main/java/nextstep/courses/domain/Course.java +++ b/src/main/java/nextstep/courses/domain/Course.java @@ -4,6 +4,7 @@ public class Course { private Long id; + private Long generation; private String title; @@ -13,17 +14,25 @@ public class Course { private LocalDateTime updatedAt; + private Sessions sessions; + public Course() { } public Course(String title, Long creatorId) { - this(0L, title, creatorId, LocalDateTime.now(), null); + this(0L, 0L, title, creatorId, new Sessions(), LocalDateTime.now(), null); } public Course(Long id, String title, Long creatorId, LocalDateTime createdAt, LocalDateTime updatedAt) { + this(id, 0L, title, creatorId, new Sessions(), createdAt, updatedAt); + } + + public Course(Long id, Long generation, String title, Long creatorId, Sessions sessions, LocalDateTime createdAt, LocalDateTime updatedAt) { this.id = id; + this.generation = generation; this.title = title; this.creatorId = creatorId; + this.sessions = sessions; this.createdAt = createdAt; this.updatedAt = updatedAt; } @@ -50,4 +59,5 @@ public String toString() { ", updatedAt=" + updatedAt + '}'; } + } diff --git a/src/main/java/nextstep/courses/domain/Sessions.java b/src/main/java/nextstep/courses/domain/Sessions.java new file mode 100644 index 000000000..4dbfc0bb1 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/Sessions.java @@ -0,0 +1,16 @@ +package nextstep.courses.domain; + +import java.util.List; + +public class Sessions { + + private List sessions; + + public Sessions() { + } + + public Sessions(List sessions) { + this.sessions = sessions; + } + +} diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java index f9122cbe3..10970910a 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java @@ -2,6 +2,7 @@ import nextstep.courses.domain.Course; import nextstep.courses.domain.CourseRepository; +import nextstep.courses.domain.Sessions; import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; diff --git a/src/main/java/nextstep/payments/domain/PaidEnrollmentPolicy.java b/src/main/java/nextstep/payments/domain/PaidEnrollmentPolicy.java index 45982a8dc..796bf39a6 100644 --- a/src/main/java/nextstep/payments/domain/PaidEnrollmentPolicy.java +++ b/src/main/java/nextstep/payments/domain/PaidEnrollmentPolicy.java @@ -2,17 +2,17 @@ public class PaidEnrollmentPolicy implements EnrollmentPolicy { - private final int capacity; + private final int maxEnrolledCount; private final int price; - public PaidEnrollmentPolicy(int capacity, int price) { - this.capacity = capacity; + public PaidEnrollmentPolicy(int maxEnrolledCount, int price) { + this.maxEnrolledCount = maxEnrolledCount; this.price = price; } @Override public boolean canEnroll(int currentEnrolledCount, Payment payment) { - return currentEnrolledCount < capacity && payment.isSameAmount((long) price); + return currentEnrolledCount < maxEnrolledCount && payment.isSameAmount((long) price); } } From 0f4391103797b161624dd7c7cc45e76a0cab09f5 Mon Sep 17 00:00:00 2001 From: "lalla.you" Date: Mon, 14 Apr 2025 00:38:08 +0900 Subject: [PATCH 8/8] =?UTF-8?q?refactor:=20step1=20=ED=94=BC=EB=93=9C?= =?UTF-8?q?=EB=B0=B1=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/nextstep/qna/domain/Answers.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/nextstep/qna/domain/Answers.java b/src/main/java/nextstep/qna/domain/Answers.java index 544cdb1cb..add1489d2 100644 --- a/src/main/java/nextstep/qna/domain/Answers.java +++ b/src/main/java/nextstep/qna/domain/Answers.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; public class Answers { private final List answers; @@ -22,16 +23,15 @@ public List getAnswers() { } public List deleteAll(NsUser loginUser) { - for (Answer answer : answers) { - if (!answer.isOwner(loginUser)) { - throw new CannotDeleteException("다른 사람이 쓴 답변이 있어 삭제할 수 없습니다."); - } + if(!canAllDelete(loginUser)) { + throw new CannotDeleteException("다른 사람이 쓴 답변이 있어 삭제할 수 없습니다."); } + return answers.stream().map(Answer::delete).collect(Collectors.toList()); + } + + private boolean canAllDelete(NsUser loginUser) { + return answers.stream().filter(answer -> answer.isOwner(loginUser)).count() + == answers.size(); - List deleteHistories = new ArrayList<>(); - for (Answer answer : answers) { - deleteHistories.add(answer.delete()); - } - return deleteHistories; } }