From 3112147da18b8e172f5f1f1872b176d02f503092 Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Tue, 9 Dec 2025 23:23:16 +0900 Subject: [PATCH 01/12] =?UTF-8?q?refactor:=20Session=EC=97=90=EC=84=9C=20S?= =?UTF-8?q?essionType,=20SessionStatus=EB=A5=BC=20=ED=95=84=EB=93=9C?= =?UTF-8?q?=EB=A1=9C=20=EA=B0=96=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../courses/domain/session/Session.java | 49 ++++++++++--------- .../infrastructure/JdbcSessionRepository.java | 15 +++--- .../nextstep/courses/domain/CourseTest.java | 6 +-- .../courses/domain/session/SessionTest.java | 2 +- .../session/service/CourseServiceTest.java | 18 ++----- 5 files changed, 39 insertions(+), 51 deletions(-) diff --git a/src/main/java/nextstep/courses/domain/session/Session.java b/src/main/java/nextstep/courses/domain/session/Session.java index 33b85cf2a..0d94be2f1 100644 --- a/src/main/java/nextstep/courses/domain/session/Session.java +++ b/src/main/java/nextstep/courses/domain/session/Session.java @@ -1,58 +1,57 @@ package nextstep.courses.domain.session; import nextstep.courses.domain.session.image.SessionImage; -import nextstep.payments.domain.Payment; import java.time.LocalDate; -import java.util.HashSet; import java.util.List; -import java.util.Set; public class Session { private final Long id; private final int cohort; private final SessionPeriod period; private final SessionImage coverImage; - private final Enrollment enrollment; + private final SessionStatus status; + private final SessionType sessionType; public Session(LocalDate startDate, LocalDate endDate, SessionImage coverImage, String status) { this(new SessionPeriod(startDate, endDate), coverImage, SessionStatus.from(status), new FreeSessionType()); } - public Session(LocalDate startDate, LocalDate endDate, SessionImage image, String status, int maximumCapacity, long fee) { - this(new SessionPeriod(startDate, endDate), image, SessionStatus.from(status), new PaidSessionType(maximumCapacity, fee)); - } - - public Session(SessionPeriod period, SessionImage coverImage, SessionStatus status, SessionType sessionType) { - this(1, period, coverImage, new Enrollment(status, sessionType)); + public Session(int cohort, LocalDate startDate, LocalDate endDate, SessionImage image, SessionStatus sessionStatus) { + this(cohort, startDate, endDate, image, sessionStatus, new FreeSessionType()); } - public Session(int cohort, LocalDate startDate, LocalDate endDate, SessionImage image) { - this(cohort, new SessionPeriod(startDate, endDate), image, new Enrollment(SessionStatus.PREPARING, new FreeSessionType())); + public Session(LocalDate startDate, LocalDate endDate, SessionImage image, String status, int maximumCapacity, long fee) { + this(new SessionPeriod(startDate, endDate), image, SessionStatus.from(status), new PaidSessionType(maximumCapacity, fee)); } - public Session(int cohort, LocalDate startDate, LocalDate endDate, SessionImage image, Enrollment enrollment) { - this(cohort, new SessionPeriod(startDate, endDate), image, enrollment); + public Session(SessionPeriod sessionPeriod, SessionImage image, SessionStatus from, SessionType sessionType) { + this(null, 1, sessionPeriod, image, from, sessionType); } - public Session(int cohort, SessionPeriod period, SessionImage coverImage, Enrollment enrollment) { - this(null, cohort, period, coverImage, enrollment); + public Session(int cohort, LocalDate startDate, LocalDate endDate, SessionImage image, SessionStatus status, SessionType type) { + this(null, cohort, new SessionPeriod(startDate, endDate), image, status, type); } - public Session(long id, int cohort, LocalDate startDate, LocalDate endDate, SessionImage image, Enrollment enrollment) { - this(id, cohort, new SessionPeriod(startDate, endDate), image, enrollment); + public Session(long id, int cohort, LocalDate startDate, LocalDate endDate, SessionImage image, SessionStatus status, SessionType type) { + this(id, cohort, new SessionPeriod(startDate, endDate), image, status, type); } - public Session(Long id, int cohort, SessionPeriod period, SessionImage coverImage, Enrollment enrollment) { + public Session(Long id, int cohort, SessionPeriod period, SessionImage coverImage, SessionStatus status, SessionType sessionType) { this.id = id; this.cohort = cohort; this.period = period; this.coverImage = coverImage; - this.enrollment = enrollment; + this.status = status; + this.sessionType = sessionType; + } + + public Session(int cohort, LocalDate startDate, LocalDate endDate, SessionImage image) { + this(cohort, startDate, endDate, image, SessionStatus.PREPARING); } public Enrollment createEnrollment(List currentStudents) { - return new Enrollment(id, enrollment.getStatus(), enrollment.getSessionType(), currentStudents); + return new Enrollment(id, status, sessionType, currentStudents); } public Long getId() { @@ -67,8 +66,12 @@ public SessionImage getImage() { return coverImage; } - public Enrollment getEnrollment() { - return enrollment; + public SessionStatus getStatus() { + return status; + } + + public SessionType getSessionType() { + return sessionType; } public LocalDate getStartDate() { diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java index e16e825eb..94943a945 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java @@ -33,9 +33,8 @@ public JdbcSessionRepository(JdbcOperations jdbcTemplate) { public void save(Long courseId, Session session) { Long imageId = saveSessionImage(session.getImage()); - Enrollment enrollment = session.getEnrollment(); - SessionStatus status = enrollment.getStatus(); - SessionType type = enrollment.getSessionType(); + SessionStatus status = session.getStatus(); + SessionType type = session.getSessionType(); String sessionTypeStr = type.isFree() ? "FREE" : "PAID"; Integer maxCapacity = null; @@ -90,7 +89,10 @@ public Sessions findByCourseId(Long courseId) { rs.getDate("start_date").toLocalDate(), rs.getDate("end_date").toLocalDate(), image, - new Enrollment(status, type)); + status, + type + + ); }, courseId); return new Sessions(sessionList); @@ -116,15 +118,14 @@ public Session findById(Long sessionId) { ? new FreeSessionType() : new PaidSessionType(rs.getInt("max_capacity"), rs.getLong("fee")); - Enrollment enrollment = new Enrollment(status, type); - return new Session( rs.getLong("id"), rs.getInt("cohort"), rs.getDate("start_date").toLocalDate(), rs.getDate("end_date").toLocalDate(), image, - enrollment); + status, + type); }, sessionId); } diff --git a/src/test/java/nextstep/courses/domain/CourseTest.java b/src/test/java/nextstep/courses/domain/CourseTest.java index 56dbbed5d..a1e45f540 100644 --- a/src/test/java/nextstep/courses/domain/CourseTest.java +++ b/src/test/java/nextstep/courses/domain/CourseTest.java @@ -1,9 +1,6 @@ package nextstep.courses.domain; -import nextstep.courses.domain.session.Enrollment; -import nextstep.courses.domain.session.FreeSessionType; import nextstep.courses.domain.session.Session; -import nextstep.courses.domain.session.SessionStatus; import nextstep.courses.domain.session.Sessions; import nextstep.courses.domain.session.image.SessionImage; import org.junit.jupiter.api.Test; @@ -12,7 +9,6 @@ import java.util.ArrayList; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.*; class CourseTest { private static final LocalDate START_DATE = LocalDate.of(2025, 11, 3); @@ -48,5 +44,5 @@ class CourseTest { assertThat(course.getSessions().size()).isEqualTo(2); } - + } diff --git a/src/test/java/nextstep/courses/domain/session/SessionTest.java b/src/test/java/nextstep/courses/domain/session/SessionTest.java index 4f8ff4e7d..5863cdccc 100644 --- a/src/test/java/nextstep/courses/domain/session/SessionTest.java +++ b/src/test/java/nextstep/courses/domain/session/SessionTest.java @@ -34,7 +34,7 @@ public class SessionTest { @Test public void 모집중_상태일때_수강신청_가능() { - Session session = new Session(1L, 1, START_DATE, END_DATE, IMAGE, new Enrollment(SessionStatus.RECRUITING, new FreeSessionType())); + Session session = new Session(1L, 1, START_DATE, END_DATE, IMAGE, SessionStatus.RECRUITING, new FreeSessionType()); Enrollment enrollment = session.createEnrollment(Collections.emptyList()); EnrolledStudent student = enrollment.enroll(1L, null); diff --git a/src/test/java/nextstep/courses/domain/session/service/CourseServiceTest.java b/src/test/java/nextstep/courses/domain/session/service/CourseServiceTest.java index 157dd887b..abf555317 100644 --- a/src/test/java/nextstep/courses/domain/session/service/CourseServiceTest.java +++ b/src/test/java/nextstep/courses/domain/session/service/CourseServiceTest.java @@ -40,18 +40,6 @@ void setUp() { courseService = new CourseService(courseRepository, sessionRepository); } - @Test - void sessions를_가진_course_저장하고_조회한다() { - Course course = createCourseTestFixture(); - - Long savedCourseId = courseService.save(course); - - Course savedCourse = courseService.findById(savedCourseId); - assertThat(savedCourse.getTitle()).isEqualTo("TDD, 클린 코드 with Java"); - assertThat(savedCourse.getSessions()).isNotNull(); - assertThat(savedCourse.getSessions().size()).isEqualTo(2); - } - @Test void 같은_이미지를_사용하는_세션들은_이미지를_재사용한다() { Course course = createCourseTestFixture(); @@ -67,10 +55,10 @@ private static Course createCourseTestFixture() { LocalDate endDate = LocalDate.of(2025, 12, 18); SessionImage image = new SessionImage(300_000L, "png", 600, 400); - Session session1 = new Session(1, new SessionPeriod(startDate, endDate), image, new Enrollment(SessionStatus.RECRUITING, new FreeSessionType())); - Session session2 = new Session(2, new SessionPeriod(startDate, endDate), image, new Enrollment(SessionStatus.RECRUITING, new FreeSessionType())); + Session session1 = new Session(startDate, endDate, image, "준비중"); + Session session2 = new Session(startDate, endDate, image, "준비중"); - Sessions sessions = new Sessions(new ArrayList<>(List.of(session1, session2))); + Sessions sessions = new Sessions(List.of(session1, session2)); return new Course("TDD, 클린 코드 with Java", 1L, sessions); } } From 909c9f024e5502dba2379199baa2315a12779dfd Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Tue, 9 Dec 2025 23:29:00 +0900 Subject: [PATCH 02/12] =?UTF-8?q?refactor:=20SessionInfo=EB=A1=9C=20?= =?UTF-8?q?=EA=B8=B0=EC=88=98,=20=EA=B8=B0=EA=B0=84,=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EC=A0=95=EB=B3=B4=20=EC=9D=98=EB=AF=B8=EC=A0=81?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=AC=B6=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../courses/domain/session/Session.java | 20 +++++------ .../courses/domain/session/SessionInfo.java | 33 +++++++++++++++++++ 2 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 src/main/java/nextstep/courses/domain/session/SessionInfo.java diff --git a/src/main/java/nextstep/courses/domain/session/Session.java b/src/main/java/nextstep/courses/domain/session/Session.java index 0d94be2f1..0327f55cc 100644 --- a/src/main/java/nextstep/courses/domain/session/Session.java +++ b/src/main/java/nextstep/courses/domain/session/Session.java @@ -7,9 +7,7 @@ public class Session { private final Long id; - private final int cohort; - private final SessionPeriod period; - private final SessionImage coverImage; + private final SessionInfo sessionInfo; private final SessionStatus status; private final SessionType sessionType; @@ -38,10 +36,12 @@ public Session(long id, int cohort, LocalDate startDate, LocalDate endDate, Sess } public Session(Long id, int cohort, SessionPeriod period, SessionImage coverImage, SessionStatus status, SessionType sessionType) { + this(id, new SessionInfo(cohort,period,coverImage),status,sessionType); + } + + public Session(Long id, SessionInfo sessionInfo, SessionStatus status, SessionType sessionType) { this.id = id; - this.cohort = cohort; - this.period = period; - this.coverImage = coverImage; + this.sessionInfo = sessionInfo; this.status = status; this.sessionType = sessionType; } @@ -59,11 +59,11 @@ public Long getId() { } public int getCohort() { - return cohort; + return sessionInfo.getCohort(); } public SessionImage getImage() { - return coverImage; + return sessionInfo.getCoverImage(); } public SessionStatus getStatus() { @@ -75,11 +75,11 @@ public SessionType getSessionType() { } public LocalDate getStartDate() { - return period.getStartDate(); + return sessionInfo.getStartDate(); } public LocalDate getEndDate() { - return period.getEndDate(); + return sessionInfo.getEndDate(); } } diff --git a/src/main/java/nextstep/courses/domain/session/SessionInfo.java b/src/main/java/nextstep/courses/domain/session/SessionInfo.java new file mode 100644 index 000000000..d9586ff84 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/session/SessionInfo.java @@ -0,0 +1,33 @@ +package nextstep.courses.domain.session; + +import nextstep.courses.domain.session.image.SessionImage; + +import java.time.LocalDate; + +public class SessionInfo { + private final int cohort; + private final SessionPeriod period; + private final SessionImage coverImage; + + public SessionInfo(int cohort, SessionPeriod period, SessionImage coverImage) { + this.cohort = cohort; + this.period = period; + this.coverImage = coverImage; + } + + public int getCohort() { + return cohort; + } + + public LocalDate getStartDate() { + return period.getStartDate(); + } + + public LocalDate getEndDate() { + return period.getEndDate(); + } + + public SessionImage getCoverImage() { + return coverImage; + } +} From 0f17c589fd90007867d14127e8b514ec68b471d6 Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Tue, 9 Dec 2025 23:57:12 +0900 Subject: [PATCH 03/12] =?UTF-8?q?docs:=204=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 --- ...24\352\265\254\354\202\254\355\225\255.md" | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 "docs/step4\354\232\224\352\265\254\354\202\254\355\225\255.md" diff --git "a/docs/step4\354\232\224\352\265\254\354\202\254\355\225\255.md" "b/docs/step4\354\232\224\352\265\254\354\202\254\355\225\255.md" new file mode 100644 index 000000000..191aaf7f2 --- /dev/null +++ "b/docs/step4\354\232\224\352\265\254\354\202\254\355\225\255.md" @@ -0,0 +1,30 @@ +# 변경된 기능 요구사항 정리 + +## 1 강의 상태 분리 +### 기존 +- 강의 진행 상태와 모집 상태가 결합되어 있음 +- 모집중 상태일 때만 수강신청 가능 + +### 변경점 +- [ ] 강의 진행 상태(ProgressStatus): 준비중, 진행중, 종료 +- [ ] 모집 상태(RecruitmentStatus): 비모집중, 모집중 +- [ ] 강의가 진행중 이여도 모집중이라면 수강신청 가능 + +## 2 강의 커버 이미지 여러개 +### 기존 +- Session은 하나의 SessionImage만 가짐 +- session테이블에 image_id가 FK 존재 + +### 변경점 +- [ ] Session은 하나 이상의 커버 이미지를 가질 수 있음 +- [ ] DB에 데이터가 존재한다는 가정하에 진행해야 하므로 session테이블의 image_id는 대표이미지로 변경 +- [ ] 등록되는 여러개의 이미지는 별도 테이블로 추가로 관리해야할듯 + +## 3 수강 승인기능 추가 +### 기존 +- 모집중이고 결제금액과 수강료가 일치하고 최대 수강인원을 넘지 않으면 수강 가능 + +### 변경점 +- [ ] 수강신청을 받으면 대기 상태가 되고 +- [ ] 강사가 선발된 인원에 대해 승인 (승인된 인원만 수강 가능) +- [ ] 강사가 선발되지 않은 인원 수강 취소 From cfa60a428aaacd9c83425162aa6cb725cca663be Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Wed, 10 Dec 2025 00:16:18 +0900 Subject: [PATCH 04/12] =?UTF-8?q?feat:=20=EA=B0=95=EC=9D=98=EC=A7=84?= =?UTF-8?q?=ED=96=89=EC=83=81=ED=83=9C=EC=99=80=20=EB=AA=A8=EC=A7=91?= =?UTF-8?q?=EC=83=81=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 --- .../domain/session/ProgressStatus.java | 26 +++++++++++++++ .../domain/session/RecruitmentStatus.java | 29 ++++++++++++++++ .../domain/session/ProgressStatusTest.java | 23 +++++++++++++ .../domain/session/RecruitmentStatusTest.java | 33 +++++++++++++++++++ 4 files changed, 111 insertions(+) create mode 100644 src/main/java/nextstep/courses/domain/session/ProgressStatus.java create mode 100644 src/main/java/nextstep/courses/domain/session/RecruitmentStatus.java create mode 100644 src/test/java/nextstep/courses/domain/session/ProgressStatusTest.java create mode 100644 src/test/java/nextstep/courses/domain/session/RecruitmentStatusTest.java diff --git a/src/main/java/nextstep/courses/domain/session/ProgressStatus.java b/src/main/java/nextstep/courses/domain/session/ProgressStatus.java new file mode 100644 index 000000000..167270ee9 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/session/ProgressStatus.java @@ -0,0 +1,26 @@ +package nextstep.courses.domain.session; + +import java.util.Arrays; + +public enum ProgressStatus { + PREPARING("준비중"), + IN_PROGRESS("진행중"), + CLOSED("종료"); + + private final String value; + + ProgressStatus(String value) { + this.value = value; + } + + public static ProgressStatus from(String description) { + return Arrays.stream(values()) + .filter(status -> status.value.equals(description)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("잘못된 진행 상태입니다: " + description)); + } + + public String getValue() { + return value; + } +} diff --git a/src/main/java/nextstep/courses/domain/session/RecruitmentStatus.java b/src/main/java/nextstep/courses/domain/session/RecruitmentStatus.java new file mode 100644 index 000000000..de0512b29 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/session/RecruitmentStatus.java @@ -0,0 +1,29 @@ +package nextstep.courses.domain.session; + +import java.util.Arrays; + +public enum RecruitmentStatus { + NOT_RECRUITING("비모집중"), + RECRUITING("모집중"); + + private final String value; + + RecruitmentStatus(String value) { + this.value = value; + } + + public static RecruitmentStatus from(String description) { + return Arrays.stream(values()) + .filter(status -> status.value.equals(description)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("잘못된 모집 상태입니다: " + description)); + } + + public boolean canEnroll() { + return this == RECRUITING; + } + + public String getValue() { + return value; + } +} diff --git a/src/test/java/nextstep/courses/domain/session/ProgressStatusTest.java b/src/test/java/nextstep/courses/domain/session/ProgressStatusTest.java new file mode 100644 index 000000000..8b781f12b --- /dev/null +++ b/src/test/java/nextstep/courses/domain/session/ProgressStatusTest.java @@ -0,0 +1,23 @@ +package nextstep.courses.domain.session; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class ProgressStatusTest { + @Test + public void 각상태별_생성확인() { + assertThat(ProgressStatus.PREPARING.getValue()).isEqualTo("준비중"); + assertThat(ProgressStatus.IN_PROGRESS.getValue()).isEqualTo("진행중"); + assertThat(ProgressStatus.CLOSED.getValue()).isEqualTo("종료"); + } + + @Test + public void 잘못된_상태_문자열이면_예외() { + assertThatThrownBy(() -> { + ProgressStatus.from("모집중"); + }).isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("잘못된 진행 상태"); + } +} diff --git a/src/test/java/nextstep/courses/domain/session/RecruitmentStatusTest.java b/src/test/java/nextstep/courses/domain/session/RecruitmentStatusTest.java new file mode 100644 index 000000000..1246e42e5 --- /dev/null +++ b/src/test/java/nextstep/courses/domain/session/RecruitmentStatusTest.java @@ -0,0 +1,33 @@ +package nextstep.courses.domain.session; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class RecruitmentStatusTest { + @Test + public void 각상태별_생성확인() { + assertThat(RecruitmentStatus.NOT_RECRUITING.getValue()).isEqualTo("비모집중"); + assertThat(RecruitmentStatus.RECRUITING.getValue()).isEqualTo("모집중"); + } + + @Test + public void 잘못된_상태_문자열이면_예외() { + assertThatThrownBy(() -> { + RecruitmentStatus.from("준비중"); + }).isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("잘못된 모집 상태"); + } + + @ParameterizedTest + @CsvSource({ + "NOT_RECRUITING, false", + "RECRUITING, true" + }) + public void 상태별_수강신청_가능여부(RecruitmentStatus status, boolean canEnroll) { + assertThat(status.canEnroll()).isEqualTo(canEnroll); + } +} From 9c1e2647eed1a2f0f5efcc1cb27dd3f4211d7be1 Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Wed, 10 Dec 2025 01:06:08 +0900 Subject: [PATCH 05/12] =?UTF-8?q?refactor:=20session=EC=97=90=20=EC=A7=84?= =?UTF-8?q?=ED=96=89=EC=83=81=ED=83=9C=EC=99=80=20=EB=AA=A8=EC=A7=91?= =?UTF-8?q?=EC=83=81=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 --- .../courses/domain/session/Enrollment.java | 27 ++++++++++++--- .../courses/domain/session/Session.java | 33 ++++++++++++++++--- .../courses/domain/session/SessionInfo.java | 4 +++ .../domain/session/ProgressStatusTest.java | 4 +-- .../domain/session/RecruitmentStatusTest.java | 6 ++-- .../courses/domain/session/SessionTest.java | 25 ++++++++++++++ 6 files changed, 86 insertions(+), 13 deletions(-) diff --git a/src/main/java/nextstep/courses/domain/session/Enrollment.java b/src/main/java/nextstep/courses/domain/session/Enrollment.java index ed61ab572..138ad4eaf 100644 --- a/src/main/java/nextstep/courses/domain/session/Enrollment.java +++ b/src/main/java/nextstep/courses/domain/session/Enrollment.java @@ -3,13 +3,12 @@ import nextstep.payments.domain.Payment; import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; public class Enrollment { private final Long sessionId; private final SessionStatus status; + private final RecruitmentStatus recruitmentStatus; private final SessionType sessionType; private final List enrolledStudents; @@ -20,12 +19,21 @@ public Enrollment(SessionStatus status, SessionType sessionType) { public Enrollment(Long sessionId, SessionStatus status, SessionType sessionType, List enrolledStudents) { this.sessionId = sessionId; this.status = status; + this.recruitmentStatus = null; + this.sessionType = sessionType; + this.enrolledStudents = enrolledStudents; + } + + public Enrollment(Long sessionId, RecruitmentStatus recruitmentStatus, SessionType sessionType, List enrolledStudents) { + this.sessionId = sessionId; + this.status = null; + this.recruitmentStatus = recruitmentStatus; this.sessionType = sessionType; this.enrolledStudents = enrolledStudents; } public EnrolledStudent enroll(Long nsUserId, Payment payment) { - if (!status.canEnroll()) { + if (!canEnroll()) { throw new IllegalStateException("모집중인 강의만 수강 신청할 수 있다"); } if (!sessionType.isValidPayment(payment)) { @@ -35,7 +43,14 @@ public EnrolledStudent enroll(Long nsUserId, Payment payment) { if (sessionType.isOverCapacity(enrolledStudents.size())) { throw new IllegalStateException("최대 수강 인원을 초과했습니다."); } - return new EnrolledStudent(sessionId,nsUserId); + return new EnrolledStudent(sessionId, nsUserId); + } + + private boolean canEnroll() { + if (recruitmentStatus != null) { + return recruitmentStatus.canEnroll(); + } + return status.canEnroll(); } public SessionStatus getStatus() { @@ -45,4 +60,8 @@ public SessionStatus getStatus() { public SessionType getSessionType() { return sessionType; } + + public RecruitmentStatus getRecruitmentStatus() { + return recruitmentStatus; + } } diff --git a/src/main/java/nextstep/courses/domain/session/Session.java b/src/main/java/nextstep/courses/domain/session/Session.java index 0327f55cc..b3c7f183b 100644 --- a/src/main/java/nextstep/courses/domain/session/Session.java +++ b/src/main/java/nextstep/courses/domain/session/Session.java @@ -9,6 +9,8 @@ public class Session { private final Long id; private final SessionInfo sessionInfo; private final SessionStatus status; + private final ProgressStatus progressStatus; + private final RecruitmentStatus recruitmentStatus; private final SessionType sessionType; public Session(LocalDate startDate, LocalDate endDate, SessionImage coverImage, String status) { @@ -36,21 +38,36 @@ public Session(long id, int cohort, LocalDate startDate, LocalDate endDate, Sess } public Session(Long id, int cohort, SessionPeriod period, SessionImage coverImage, SessionStatus status, SessionType sessionType) { - this(id, new SessionInfo(cohort,period,coverImage),status,sessionType); + this(id, new SessionInfo(cohort, period, coverImage), status, sessionType); } public Session(Long id, SessionInfo sessionInfo, SessionStatus status, SessionType sessionType) { + this(id, sessionInfo, status, null, null, sessionType); + } + + + public Session(Long id, int cohort, LocalDate startDate, LocalDate endDate, SessionImage image, ProgressStatus progressStatus, RecruitmentStatus recruitmentStatus, SessionType sessionType) { + this(id, new SessionInfo(cohort, startDate, endDate, image), null, progressStatus, recruitmentStatus, sessionType); + } + + public Session(int cohort, LocalDate startDate, LocalDate endDate, SessionImage image) { + this(cohort, startDate, endDate, image, SessionStatus.PREPARING); + } + + public Session(Long id, SessionInfo sessionInfo, SessionStatus status, ProgressStatus progressStatus, RecruitmentStatus recruitmentStatus, SessionType sessionType) { this.id = id; this.sessionInfo = sessionInfo; this.status = status; + this.progressStatus = progressStatus; + this.recruitmentStatus = recruitmentStatus; this.sessionType = sessionType; } - public Session(int cohort, LocalDate startDate, LocalDate endDate, SessionImage image) { - this(cohort, startDate, endDate, image, SessionStatus.PREPARING); - } public Enrollment createEnrollment(List currentStudents) { + if (recruitmentStatus != null) { + return new Enrollment(id, recruitmentStatus, sessionType, currentStudents); + } return new Enrollment(id, status, sessionType, currentStudents); } @@ -74,6 +91,14 @@ public SessionType getSessionType() { return sessionType; } + public ProgressStatus getProgressStatus() { + return progressStatus; + } + + public RecruitmentStatus getRecruitmentStatus() { + return recruitmentStatus; + } + public LocalDate getStartDate() { return sessionInfo.getStartDate(); } diff --git a/src/main/java/nextstep/courses/domain/session/SessionInfo.java b/src/main/java/nextstep/courses/domain/session/SessionInfo.java index d9586ff84..98a090fbc 100644 --- a/src/main/java/nextstep/courses/domain/session/SessionInfo.java +++ b/src/main/java/nextstep/courses/domain/session/SessionInfo.java @@ -9,6 +9,10 @@ public class SessionInfo { private final SessionPeriod period; private final SessionImage coverImage; + public SessionInfo(int cohort, LocalDate startDate, LocalDate endDate, SessionImage image) { + this(cohort, new SessionPeriod(startDate, endDate), image); + } + public SessionInfo(int cohort, SessionPeriod period, SessionImage coverImage) { this.cohort = cohort; this.period = period; diff --git a/src/test/java/nextstep/courses/domain/session/ProgressStatusTest.java b/src/test/java/nextstep/courses/domain/session/ProgressStatusTest.java index 8b781f12b..b91628226 100644 --- a/src/test/java/nextstep/courses/domain/session/ProgressStatusTest.java +++ b/src/test/java/nextstep/courses/domain/session/ProgressStatusTest.java @@ -7,14 +7,14 @@ public class ProgressStatusTest { @Test - public void 각상태별_생성확인() { + void 각상태별_생성확인() { assertThat(ProgressStatus.PREPARING.getValue()).isEqualTo("준비중"); assertThat(ProgressStatus.IN_PROGRESS.getValue()).isEqualTo("진행중"); assertThat(ProgressStatus.CLOSED.getValue()).isEqualTo("종료"); } @Test - public void 잘못된_상태_문자열이면_예외() { + void 잘못된_상태_문자열이면_예외() { assertThatThrownBy(() -> { ProgressStatus.from("모집중"); }).isInstanceOf(IllegalArgumentException.class) diff --git a/src/test/java/nextstep/courses/domain/session/RecruitmentStatusTest.java b/src/test/java/nextstep/courses/domain/session/RecruitmentStatusTest.java index 1246e42e5..a83d01891 100644 --- a/src/test/java/nextstep/courses/domain/session/RecruitmentStatusTest.java +++ b/src/test/java/nextstep/courses/domain/session/RecruitmentStatusTest.java @@ -9,13 +9,13 @@ public class RecruitmentStatusTest { @Test - public void 각상태별_생성확인() { + void 각상태별_생성확인() { assertThat(RecruitmentStatus.NOT_RECRUITING.getValue()).isEqualTo("비모집중"); assertThat(RecruitmentStatus.RECRUITING.getValue()).isEqualTo("모집중"); } @Test - public void 잘못된_상태_문자열이면_예외() { + void 잘못된_상태_문자열이면_예외() { assertThatThrownBy(() -> { RecruitmentStatus.from("준비중"); }).isInstanceOf(IllegalArgumentException.class) @@ -27,7 +27,7 @@ public class RecruitmentStatusTest { "NOT_RECRUITING, false", "RECRUITING, true" }) - public void 상태별_수강신청_가능여부(RecruitmentStatus status, boolean canEnroll) { + void 상태별_수강신청_가능여부(RecruitmentStatus status, boolean canEnroll) { assertThat(status.canEnroll()).isEqualTo(canEnroll); } } diff --git a/src/test/java/nextstep/courses/domain/session/SessionTest.java b/src/test/java/nextstep/courses/domain/session/SessionTest.java index 5863cdccc..b7df0d0b1 100644 --- a/src/test/java/nextstep/courses/domain/session/SessionTest.java +++ b/src/test/java/nextstep/courses/domain/session/SessionTest.java @@ -43,4 +43,29 @@ public class SessionTest { } + + @Test + void 진행중이면서_모집중일때_수강신청_가능() { + Session session = new Session(1L, 1, START_DATE, END_DATE, IMAGE, + ProgressStatus.IN_PROGRESS, RecruitmentStatus.RECRUITING, new FreeSessionType()); + + Enrollment enrollment = session.createEnrollment(java.util.Collections.emptyList()); + EnrolledStudent student = enrollment.enroll(1L, null); + + assertThat(student.getNsUserId()).isEqualTo(1L); + assertThat(student.getSessionId()).isEqualTo(1L); + } + + @Test + void 기존_SessionStatus만있어도_생성_가능() { + LocalDate startDate = LocalDate.of(2024, 1, 1); + LocalDate endDate = LocalDate.of(2024, 3, 31); + SessionImage image = new SessionImage(500_000L, "png", 900, 600); + + Session session = new Session(1L, 1, startDate, endDate, image, SessionStatus.RECRUITING, new FreeSessionType()); + + assertThat(session.getStatus()).isEqualTo(SessionStatus.RECRUITING); + } + + } From aa5c6906a2a9d75d566fdb9236f7d97d61dfe9c7 Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Fri, 12 Dec 2025 13:58:38 +0900 Subject: [PATCH 06/12] =?UTF-8?q?refactor:=20session=20=EC=8A=A4=ED=82=A4?= =?UTF-8?q?=EB=A7=88=EC=97=90=20=EB=91=90=20=EC=83=81=ED=83=9C=EA=B0=92=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/schema.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 5adaefd5d..a19ff21e2 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -70,6 +70,8 @@ create table session end_date date not null, image_id bigint not null, status varchar(20) not null, + progress_status varchar(20), + recruitment_status varchar(20), session_type varchar(20) not null, max_capacity int, fee bigint, From 039d58d0611960a363420c91683ef5b82eaf5212 Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Sun, 14 Dec 2025 15:27:10 +0900 Subject: [PATCH 07/12] =?UTF-8?q?refactor:=20jdbcSessionRepository?= =?UTF-8?q?=EC=97=90=20=EC=B6=94=EA=B0=80=EC=BB=AC=EB=9F=BC=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/JdbcSessionRepository.java | 48 +++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java index 94943a945..dd56ae545 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java @@ -1,8 +1,9 @@ package nextstep.courses.infrastructure; -import nextstep.courses.domain.session.Enrollment; import nextstep.courses.domain.session.FreeSessionType; import nextstep.courses.domain.session.PaidSessionType; +import nextstep.courses.domain.session.ProgressStatus; +import nextstep.courses.domain.session.RecruitmentStatus; import nextstep.courses.domain.session.Session; import nextstep.courses.domain.session.SessionRepository; import nextstep.courses.domain.session.SessionStatus; @@ -33,7 +34,6 @@ public JdbcSessionRepository(JdbcOperations jdbcTemplate) { public void save(Long courseId, Session session) { Long imageId = saveSessionImage(session.getImage()); - SessionStatus status = session.getStatus(); SessionType type = session.getSessionType(); String sessionTypeStr = type.isFree() ? "FREE" : "PAID"; @@ -46,8 +46,21 @@ public void save(Long courseId, Session session) { fee = paidType.getFee(); } - String sql = "insert into session (course_id, cohort, start_date, end_date, image_id, status, session_type, max_capacity, fee, created_at) " + - "values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + String statusValue; + String progressStatusValue = null; + String recruitmentStatusValue = null; + + if (session.getProgressStatus() != null && session.getRecruitmentStatus() != null) { + progressStatusValue = session.getProgressStatus().getValue(); + recruitmentStatusValue = session.getRecruitmentStatus().getValue(); + statusValue = session.getRecruitmentStatus().canEnroll() ? "모집중" : session.getProgressStatus().getValue(); + } else { + statusValue = session.getStatus().getValue(); + } + + String sql = "insert into session (course_id, cohort, start_date, end_date, image_id, status, progress_status, recruitment_status, session_type, max_capacity, fee, created_at)" + + "values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + jdbcTemplate.update(sql, courseId, @@ -55,7 +68,9 @@ public void save(Long courseId, Session session) { Date.valueOf(session.getStartDate()), Date.valueOf(session.getEndDate()), imageId, - status.getValue(), + statusValue, + progressStatusValue, + recruitmentStatusValue, sessionTypeStr, maxCapacity, fee, @@ -65,7 +80,8 @@ public void save(Long courseId, Session session) { @Override public Sessions findByCourseId(Long courseId) { - String sql = "select s.id, s.cohort, s.start_date, s.end_date, s.status, s.session_type, s.max_capacity, s.fee, " + + String sql = "select s.id, s.cohort, s.start_date, s.end_date, s.status, s.progress_status, s.recruitment_status " + + "s.session_type, s.max_capacity, s.fee, " + "i.file_size, i.image_type, i.width, i.height " + "from session s " + "join session_image i on s.image_id = i.id " + @@ -79,20 +95,32 @@ public Sessions findByCourseId(Long courseId) { rs.getInt("width"), rs.getInt("height")); - SessionStatus status = SessionStatus.from(rs.getString("status")); SessionType type = "FREE".equals(rs.getString("session_type")) ? new FreeSessionType() : new PaidSessionType(rs.getInt("max_capacity"), rs.getLong("fee")); + String progressStatusValue = rs.getString("progress_status"); + String recruitmentStatusValue = rs.getString("recruitment_status"); + if (progressStatusValue != null && recruitmentStatusValue != null) { + return new Session( + rs.getLong("id"), + rs.getInt("cohort"), + rs.getDate("start_date").toLocalDate(), + rs.getDate("end_date").toLocalDate(), + image, + ProgressStatus.from(progressStatusValue), + RecruitmentStatus.from(recruitmentStatusValue), + type); + } + SessionStatus status = SessionStatus.from(rs.getString("status")); return new Session( + rs.getLong("id"), rs.getInt("cohort"), rs.getDate("start_date").toLocalDate(), rs.getDate("end_date").toLocalDate(), image, status, - type - - ); + type); }, courseId); return new Sessions(sessionList); From 0bd3a15c8f6825ffae347b6392bdce075035dae3 Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Mon, 15 Dec 2025 00:47:36 +0900 Subject: [PATCH 08/12] =?UTF-8?q?refactor:=20SessionStatus=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0,=20=EB=B0=8F=20=EC=83=9D=EC=84=B1=EC=9E=90=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../courses/domain/session/Enrollment.java | 26 +------- .../courses/domain/session/Session.java | 59 +++++-------------- .../infrastructure/JdbcSessionRepository.java | 41 ++++--------- .../nextstep/courses/domain/CourseTest.java | 11 ++-- .../domain/session/EnrollmentTest.java | 8 +-- .../courses/domain/session/SessionTest.java | 15 +++-- .../courses/domain/session/SessionsTest.java | 12 ++-- .../session/service/CourseServiceTest.java | 9 +-- 8 files changed, 57 insertions(+), 124 deletions(-) diff --git a/src/main/java/nextstep/courses/domain/session/Enrollment.java b/src/main/java/nextstep/courses/domain/session/Enrollment.java index 138ad4eaf..6f3c0fe75 100644 --- a/src/main/java/nextstep/courses/domain/session/Enrollment.java +++ b/src/main/java/nextstep/courses/domain/session/Enrollment.java @@ -7,33 +7,24 @@ public class Enrollment { private final Long sessionId; - private final SessionStatus status; private final RecruitmentStatus recruitmentStatus; private final SessionType sessionType; private final List enrolledStudents; - public Enrollment(SessionStatus status, SessionType sessionType) { - this(null, status, sessionType, Collections.emptyList()); + public Enrollment(RecruitmentStatus recruitmentStatus, SessionType sessionType) { + this(null, recruitmentStatus, sessionType, Collections.emptyList()); } - public Enrollment(Long sessionId, SessionStatus status, SessionType sessionType, List enrolledStudents) { - this.sessionId = sessionId; - this.status = status; - this.recruitmentStatus = null; - this.sessionType = sessionType; - this.enrolledStudents = enrolledStudents; - } public Enrollment(Long sessionId, RecruitmentStatus recruitmentStatus, SessionType sessionType, List enrolledStudents) { this.sessionId = sessionId; - this.status = null; this.recruitmentStatus = recruitmentStatus; this.sessionType = sessionType; this.enrolledStudents = enrolledStudents; } public EnrolledStudent enroll(Long nsUserId, Payment payment) { - if (!canEnroll()) { + if (!recruitmentStatus.canEnroll()) { throw new IllegalStateException("모집중인 강의만 수강 신청할 수 있다"); } if (!sessionType.isValidPayment(payment)) { @@ -46,17 +37,6 @@ public EnrolledStudent enroll(Long nsUserId, Payment payment) { return new EnrolledStudent(sessionId, nsUserId); } - private boolean canEnroll() { - if (recruitmentStatus != null) { - return recruitmentStatus.canEnroll(); - } - return status.canEnroll(); - } - - public SessionStatus getStatus() { - return status; - } - public SessionType getSessionType() { return sessionType; } diff --git a/src/main/java/nextstep/courses/domain/session/Session.java b/src/main/java/nextstep/courses/domain/session/Session.java index b3c7f183b..438a2205e 100644 --- a/src/main/java/nextstep/courses/domain/session/Session.java +++ b/src/main/java/nextstep/courses/domain/session/Session.java @@ -8,56 +8,34 @@ public class Session { private final Long id; private final SessionInfo sessionInfo; - private final SessionStatus status; private final ProgressStatus progressStatus; private final RecruitmentStatus recruitmentStatus; private final SessionType sessionType; - public Session(LocalDate startDate, LocalDate endDate, SessionImage coverImage, String status) { - this(new SessionPeriod(startDate, endDate), coverImage, SessionStatus.from(status), new FreeSessionType()); + public Session(LocalDate startDate, LocalDate endDate, SessionImage image) { + this(new SessionInfo(1, startDate, endDate, image), + ProgressStatus.PREPARING, + RecruitmentStatus.NOT_RECRUITING, + new FreeSessionType()); } - public Session(int cohort, LocalDate startDate, LocalDate endDate, SessionImage image, SessionStatus sessionStatus) { - this(cohort, startDate, endDate, image, sessionStatus, new FreeSessionType()); + public Session(int cohort, LocalDate startDate, LocalDate endDate, SessionImage image, + ProgressStatus progressStatus, RecruitmentStatus recruitmentStatus, SessionType type) { + this(null, new SessionInfo(cohort, startDate, endDate, image), progressStatus, recruitmentStatus, type); } - public Session(LocalDate startDate, LocalDate endDate, SessionImage image, String status, int maximumCapacity, long fee) { - this(new SessionPeriod(startDate, endDate), image, SessionStatus.from(status), new PaidSessionType(maximumCapacity, fee)); + public Session(Long id, int cohort, LocalDate startDate, LocalDate endDate, SessionImage image, + ProgressStatus progressStatus, RecruitmentStatus recruitmentStatus, SessionType type) { + this(id, new SessionInfo(cohort, startDate, endDate, image), progressStatus, recruitmentStatus, type); } - public Session(SessionPeriod sessionPeriod, SessionImage image, SessionStatus from, SessionType sessionType) { - this(null, 1, sessionPeriod, image, from, sessionType); + public Session(SessionInfo info, ProgressStatus progressStatus, RecruitmentStatus recruitmentStatus, SessionType type) { + this(null, info, progressStatus, recruitmentStatus, type); } - public Session(int cohort, LocalDate startDate, LocalDate endDate, SessionImage image, SessionStatus status, SessionType type) { - this(null, cohort, new SessionPeriod(startDate, endDate), image, status, type); - } - - public Session(long id, int cohort, LocalDate startDate, LocalDate endDate, SessionImage image, SessionStatus status, SessionType type) { - this(id, cohort, new SessionPeriod(startDate, endDate), image, status, type); - } - - public Session(Long id, int cohort, SessionPeriod period, SessionImage coverImage, SessionStatus status, SessionType sessionType) { - this(id, new SessionInfo(cohort, period, coverImage), status, sessionType); - } - - public Session(Long id, SessionInfo sessionInfo, SessionStatus status, SessionType sessionType) { - this(id, sessionInfo, status, null, null, sessionType); - } - - - public Session(Long id, int cohort, LocalDate startDate, LocalDate endDate, SessionImage image, ProgressStatus progressStatus, RecruitmentStatus recruitmentStatus, SessionType sessionType) { - this(id, new SessionInfo(cohort, startDate, endDate, image), null, progressStatus, recruitmentStatus, sessionType); - } - - public Session(int cohort, LocalDate startDate, LocalDate endDate, SessionImage image) { - this(cohort, startDate, endDate, image, SessionStatus.PREPARING); - } - - public Session(Long id, SessionInfo sessionInfo, SessionStatus status, ProgressStatus progressStatus, RecruitmentStatus recruitmentStatus, SessionType sessionType) { + public Session(Long id, SessionInfo sessionInfo, ProgressStatus progressStatus, RecruitmentStatus recruitmentStatus, SessionType sessionType) { this.id = id; this.sessionInfo = sessionInfo; - this.status = status; this.progressStatus = progressStatus; this.recruitmentStatus = recruitmentStatus; this.sessionType = sessionType; @@ -65,10 +43,7 @@ public Session(Long id, SessionInfo sessionInfo, SessionStatus status, ProgressS public Enrollment createEnrollment(List currentStudents) { - if (recruitmentStatus != null) { - return new Enrollment(id, recruitmentStatus, sessionType, currentStudents); - } - return new Enrollment(id, status, sessionType, currentStudents); + return new Enrollment(id, recruitmentStatus, sessionType, currentStudents); } public Long getId() { @@ -83,10 +58,6 @@ public SessionImage getImage() { return sessionInfo.getCoverImage(); } - public SessionStatus getStatus() { - return status; - } - public SessionType getSessionType() { return sessionType; } diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java index dd56ae545..ac0a02e5d 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java @@ -46,17 +46,9 @@ public void save(Long courseId, Session session) { fee = paidType.getFee(); } - String statusValue; - String progressStatusValue = null; - String recruitmentStatusValue = null; - - if (session.getProgressStatus() != null && session.getRecruitmentStatus() != null) { - progressStatusValue = session.getProgressStatus().getValue(); - recruitmentStatusValue = session.getRecruitmentStatus().getValue(); - statusValue = session.getRecruitmentStatus().canEnroll() ? "모집중" : session.getProgressStatus().getValue(); - } else { - statusValue = session.getStatus().getValue(); - } + String progressStatusValue = session.getProgressStatus().getValue(); + String recruitmentStatusValue = session.getRecruitmentStatus().getValue(); + String statusValue = session.getRecruitmentStatus().canEnroll() ? "모집중" : session.getProgressStatus().getValue(); String sql = "insert into session (course_id, cohort, start_date, end_date, image_id, status, progress_status, recruitment_status, session_type, max_capacity, fee, created_at)" + "values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; @@ -99,27 +91,17 @@ public Sessions findByCourseId(Long courseId) { ? new FreeSessionType() : new PaidSessionType(rs.getInt("max_capacity"), rs.getLong("fee")); - String progressStatusValue = rs.getString("progress_status"); - String recruitmentStatusValue = rs.getString("recruitment_status"); - if (progressStatusValue != null && recruitmentStatusValue != null) { - return new Session( - rs.getLong("id"), - rs.getInt("cohort"), - rs.getDate("start_date").toLocalDate(), - rs.getDate("end_date").toLocalDate(), - image, - ProgressStatus.from(progressStatusValue), - RecruitmentStatus.from(recruitmentStatusValue), - type); - } - SessionStatus status = SessionStatus.from(rs.getString("status")); + ProgressStatus progressStatus = ProgressStatus.from(rs.getString("progress_status")); + RecruitmentStatus recruitmentStatus = RecruitmentStatus.from(rs.getString("recruitment_status")); + return new Session( rs.getLong("id"), rs.getInt("cohort"), rs.getDate("start_date").toLocalDate(), rs.getDate("end_date").toLocalDate(), image, - status, + progressStatus, + recruitmentStatus, type); }, courseId); @@ -141,7 +123,9 @@ public Session findById(Long sessionId) { rs.getInt("width"), rs.getInt("height")); - SessionStatus status = SessionStatus.from(rs.getString("status")); + ProgressStatus progressStatus = ProgressStatus.from(rs.getString("progress_status")); + RecruitmentStatus recruitmentStatus = RecruitmentStatus.from(rs.getString("recruitment_status")); + SessionType type = "FREE".equals(rs.getString("session_type")) ? new FreeSessionType() : new PaidSessionType(rs.getInt("max_capacity"), rs.getLong("fee")); @@ -152,7 +136,8 @@ public Session findById(Long sessionId) { rs.getDate("start_date").toLocalDate(), rs.getDate("end_date").toLocalDate(), image, - status, + progressStatus, + recruitmentStatus, type); }, sessionId); } diff --git a/src/test/java/nextstep/courses/domain/CourseTest.java b/src/test/java/nextstep/courses/domain/CourseTest.java index a1e45f540..213ed54fb 100644 --- a/src/test/java/nextstep/courses/domain/CourseTest.java +++ b/src/test/java/nextstep/courses/domain/CourseTest.java @@ -1,5 +1,8 @@ package nextstep.courses.domain; +import nextstep.courses.domain.session.FreeSessionType; +import nextstep.courses.domain.session.ProgressStatus; +import nextstep.courses.domain.session.RecruitmentStatus; import nextstep.courses.domain.session.Session; import nextstep.courses.domain.session.Sessions; import nextstep.courses.domain.session.image.SessionImage; @@ -18,8 +21,8 @@ class CourseTest { @Test public void 세션을_가진_과정_생성() { - Session session1 = new Session(1, START_DATE, END_DATE, IMAGE); - Session session2 = new Session(2, START_DATE, END_DATE, IMAGE); + Session session1 = new Session(1, START_DATE, END_DATE, IMAGE, ProgressStatus.PREPARING, RecruitmentStatus.RECRUITING, new FreeSessionType()); + Session session2 = new Session(2, START_DATE, END_DATE, IMAGE, ProgressStatus.PREPARING, RecruitmentStatus.NOT_RECRUITING, new FreeSessionType()); Sessions sessions = new Sessions(new ArrayList<>()); sessions.add(session1); sessions.add(session2); @@ -33,13 +36,13 @@ class CourseTest { @Test public void 과정에_새로운_기수_추가() { - Session session1 = new Session(1, START_DATE, END_DATE, IMAGE); + Session session1 = new Session(1, START_DATE, END_DATE, IMAGE,ProgressStatus.PREPARING, RecruitmentStatus.RECRUITING,new FreeSessionType()); Sessions sessions = new Sessions(new ArrayList<>()); sessions.add(session1); Course course = new Course("JPA의사실과 오해", 1L, sessions); - Session session2 = new Session(2, START_DATE, END_DATE, IMAGE); + Session session2 = new Session(2, START_DATE, END_DATE, IMAGE,ProgressStatus.PREPARING, RecruitmentStatus.RECRUITING,new FreeSessionType()); course.addSession(session2); assertThat(course.getSessions().size()).isEqualTo(2); diff --git a/src/test/java/nextstep/courses/domain/session/EnrollmentTest.java b/src/test/java/nextstep/courses/domain/session/EnrollmentTest.java index 1796627b5..438e25af0 100644 --- a/src/test/java/nextstep/courses/domain/session/EnrollmentTest.java +++ b/src/test/java/nextstep/courses/domain/session/EnrollmentTest.java @@ -12,7 +12,7 @@ public class EnrollmentTest { @Test public void 모집중_상태일때_수강신청_가능() { - Enrollment enrollment = new Enrollment(1L, SessionStatus.RECRUITING, new FreeSessionType(), Collections.emptyList()); + Enrollment enrollment = new Enrollment(1L, RecruitmentStatus.RECRUITING, new FreeSessionType(), Collections.emptyList()); EnrolledStudent student = enrollment.enroll(1L, null); @@ -22,7 +22,7 @@ public class EnrollmentTest { @Test public void 준비중_상태일때_수강신청_불가() { - Enrollment enrollment = new Enrollment(SessionStatus.PREPARING, new FreeSessionType()); + Enrollment enrollment = new Enrollment(RecruitmentStatus.NOT_RECRUITING, new FreeSessionType()); assertThatThrownBy(() -> { enrollment.enroll(1L, null); @@ -33,7 +33,7 @@ public class EnrollmentTest { @Test public void 유료_강의_결제금액_검증() { SessionType type = new PaidSessionType(10, 100_000L); - Enrollment enrollment = new Enrollment(1L, SessionStatus.RECRUITING, type, Collections.emptyList()); + Enrollment enrollment = new Enrollment(1L, RecruitmentStatus.RECRUITING, type, Collections.emptyList()); assertThatThrownBy(() -> enrollment.enroll(1L, new Payment("결제번호-1", 1L, 1L, 50_000L))) .isInstanceOf(IllegalArgumentException.class) @@ -50,7 +50,7 @@ public class EnrollmentTest { new EnrolledStudent(1L, 2L) ); - Enrollment enrollment = new Enrollment(1L, SessionStatus.RECRUITING, type, currentStudent); + Enrollment enrollment = new Enrollment(1L, RecruitmentStatus.RECRUITING, type, currentStudent); assertThatThrownBy(() -> { enrollment.enroll(3L, new Payment("결제번호-1", 1L, 3L, fee)); diff --git a/src/test/java/nextstep/courses/domain/session/SessionTest.java b/src/test/java/nextstep/courses/domain/session/SessionTest.java index b7df0d0b1..814b92aeb 100644 --- a/src/test/java/nextstep/courses/domain/session/SessionTest.java +++ b/src/test/java/nextstep/courses/domain/session/SessionTest.java @@ -1,14 +1,12 @@ package nextstep.courses.domain.session; import nextstep.courses.domain.session.image.SessionImage; -import nextstep.payments.domain.Payment; import org.junit.jupiter.api.Test; import java.time.LocalDate; import java.util.Collections; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; public class SessionTest { private static final LocalDate START_DATE = LocalDate.of(2025, 11, 3); @@ -17,7 +15,7 @@ public class SessionTest { @Test public void 정상적인_강의_생성() { - Session session = new Session(START_DATE, END_DATE, IMAGE, "준비중"); + Session session = new Session(START_DATE, END_DATE, IMAGE); assertThat(session).isNotNull(); } @@ -26,7 +24,7 @@ public class SessionTest { public void 기수_정보를_가진_강의_생성() { int cohort = 1; - Session session = new Session(cohort, START_DATE, END_DATE, IMAGE); + Session session = new Session(cohort, START_DATE, END_DATE, IMAGE, ProgressStatus.IN_PROGRESS, RecruitmentStatus.RECRUITING, new FreeSessionType()); assertThat(session.getCohort()).isEqualTo(cohort); } @@ -34,7 +32,8 @@ public class SessionTest { @Test public void 모집중_상태일때_수강신청_가능() { - Session session = new Session(1L, 1, START_DATE, END_DATE, IMAGE, SessionStatus.RECRUITING, new FreeSessionType()); + Session session = new Session(1L, 1, START_DATE, END_DATE, IMAGE, + ProgressStatus.PREPARING, RecruitmentStatus.RECRUITING, new FreeSessionType()); Enrollment enrollment = session.createEnrollment(Collections.emptyList()); EnrolledStudent student = enrollment.enroll(1L, null); @@ -43,7 +42,6 @@ public class SessionTest { } - @Test void 진행중이면서_모집중일때_수강신청_가능() { Session session = new Session(1L, 1, START_DATE, END_DATE, IMAGE, @@ -62,9 +60,10 @@ public class SessionTest { LocalDate endDate = LocalDate.of(2024, 3, 31); SessionImage image = new SessionImage(500_000L, "png", 900, 600); - Session session = new Session(1L, 1, startDate, endDate, image, SessionStatus.RECRUITING, new FreeSessionType()); + Session session = new Session(1L, 1, startDate, endDate, image, + ProgressStatus.PREPARING, RecruitmentStatus.RECRUITING, new FreeSessionType()); - assertThat(session.getStatus()).isEqualTo(SessionStatus.RECRUITING); + assertThat(session.getRecruitmentStatus()).isEqualTo(RecruitmentStatus.RECRUITING); } diff --git a/src/test/java/nextstep/courses/domain/session/SessionsTest.java b/src/test/java/nextstep/courses/domain/session/SessionsTest.java index 7e1abc292..08ea8f920 100644 --- a/src/test/java/nextstep/courses/domain/session/SessionsTest.java +++ b/src/test/java/nextstep/courses/domain/session/SessionsTest.java @@ -15,8 +15,8 @@ class SessionsTest { @Test public void 세션_목록_생성() { - Session session1 = new Session(1, START_DATE, END_DATE, IMAGE); - Session session2 = new Session(2, START_DATE, END_DATE, IMAGE); + Session session1 = new Session(1, START_DATE, END_DATE, IMAGE, ProgressStatus.PREPARING, RecruitmentStatus.RECRUITING, new FreeSessionType()); + Session session2 = new Session(2, START_DATE, END_DATE, IMAGE, ProgressStatus.PREPARING, RecruitmentStatus.RECRUITING, new FreeSessionType()); Sessions sessions = new Sessions(List.of(session1, session2)); @@ -27,10 +27,10 @@ class SessionsTest { public void 세션_추가() { SessionImage image = new SessionImage(500_000L, "png", 900, 600); - Session session1 = new Session(1, START_DATE, END_DATE, image); + Session session1 = new Session(1, START_DATE, END_DATE, image, ProgressStatus.PREPARING, RecruitmentStatus.RECRUITING, new FreeSessionType()); Sessions sessions = new Sessions(List.of(session1)); - Session session2 = new Session(2, START_DATE, END_DATE, image); + Session session2 = new Session(2, START_DATE, END_DATE, image, ProgressStatus.PREPARING, RecruitmentStatus.RECRUITING, new FreeSessionType()); sessions.add(session2); assertThat(sessions.size()).isEqualTo(2); @@ -38,8 +38,8 @@ class SessionsTest { @Test public void 특정_기수_세션_조회() { - Session session1 = new Session(1, START_DATE, END_DATE, IMAGE); - Session session2 = new Session(2, START_DATE, END_DATE, IMAGE); + Session session1 = new Session(1, START_DATE, END_DATE, IMAGE, ProgressStatus.PREPARING, RecruitmentStatus.RECRUITING, new FreeSessionType()); + Session session2 = new Session(2, START_DATE, END_DATE, IMAGE, ProgressStatus.PREPARING, RecruitmentStatus.RECRUITING, new FreeSessionType()); Sessions sessions = new Sessions(List.of(session1, session2)); Session found = sessions.findByCohort(2); diff --git a/src/test/java/nextstep/courses/domain/session/service/CourseServiceTest.java b/src/test/java/nextstep/courses/domain/session/service/CourseServiceTest.java index abf555317..4a6c4b32b 100644 --- a/src/test/java/nextstep/courses/domain/session/service/CourseServiceTest.java +++ b/src/test/java/nextstep/courses/domain/session/service/CourseServiceTest.java @@ -2,12 +2,8 @@ import nextstep.courses.domain.Course; import nextstep.courses.domain.CourseRepository; -import nextstep.courses.domain.session.Enrollment; -import nextstep.courses.domain.session.FreeSessionType; import nextstep.courses.domain.session.Session; -import nextstep.courses.domain.session.SessionPeriod; import nextstep.courses.domain.session.SessionRepository; -import nextstep.courses.domain.session.SessionStatus; import nextstep.courses.domain.session.Sessions; import nextstep.courses.domain.session.image.SessionImage; import nextstep.courses.infrastructure.JdbcCourseRepository; @@ -20,7 +16,6 @@ import org.springframework.jdbc.core.JdbcTemplate; import java.time.LocalDate; -import java.util.ArrayList; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -55,8 +50,8 @@ private static Course createCourseTestFixture() { LocalDate endDate = LocalDate.of(2025, 12, 18); SessionImage image = new SessionImage(300_000L, "png", 600, 400); - Session session1 = new Session(startDate, endDate, image, "준비중"); - Session session2 = new Session(startDate, endDate, image, "준비중"); + Session session1 = new Session(startDate, endDate, image); + Session session2 = new Session(startDate, endDate, image); Sessions sessions = new Sessions(List.of(session1, session2)); return new Course("TDD, 클린 코드 with Java", 1L, sessions); From ce1982021c2081938226317b52eb248733a3bd4f Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Mon, 15 Dec 2025 00:58:44 +0900 Subject: [PATCH 09/12] =?UTF-8?q?feat:=20=EC=84=B8=EC=85=98=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=EC=9D=BC=EA=B8=89=EC=BB=AC=EB=A0=89?= =?UTF-8?q?=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../courses/domain/session/Session.java | 7 +++- .../courses/domain/session/SessionInfo.java | 17 +++++--- .../domain/session/image/SessionImages.java | 32 +++++++++++++++ .../session/image/SessionImagesTest.java | 40 +++++++++++++++++++ 4 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 src/main/java/nextstep/courses/domain/session/image/SessionImages.java create mode 100644 src/test/java/nextstep/courses/domain/session/image/SessionImagesTest.java diff --git a/src/main/java/nextstep/courses/domain/session/Session.java b/src/main/java/nextstep/courses/domain/session/Session.java index 438a2205e..98a1036d1 100644 --- a/src/main/java/nextstep/courses/domain/session/Session.java +++ b/src/main/java/nextstep/courses/domain/session/Session.java @@ -1,6 +1,7 @@ package nextstep.courses.domain.session; import nextstep.courses.domain.session.image.SessionImage; +import nextstep.courses.domain.session.image.SessionImages; import java.time.LocalDate; import java.util.List; @@ -55,7 +56,11 @@ public int getCohort() { } public SessionImage getImage() { - return sessionInfo.getCoverImage(); + return sessionInfo.getImage(); + } + + public SessionImages getImages() { + return sessionInfo.getImages(); } public SessionType getSessionType() { diff --git a/src/main/java/nextstep/courses/domain/session/SessionInfo.java b/src/main/java/nextstep/courses/domain/session/SessionInfo.java index 98a090fbc..138a747ca 100644 --- a/src/main/java/nextstep/courses/domain/session/SessionInfo.java +++ b/src/main/java/nextstep/courses/domain/session/SessionInfo.java @@ -1,22 +1,23 @@ package nextstep.courses.domain.session; import nextstep.courses.domain.session.image.SessionImage; +import nextstep.courses.domain.session.image.SessionImages; import java.time.LocalDate; public class SessionInfo { private final int cohort; private final SessionPeriod period; - private final SessionImage coverImage; + private final SessionImages images; public SessionInfo(int cohort, LocalDate startDate, LocalDate endDate, SessionImage image) { - this(cohort, new SessionPeriod(startDate, endDate), image); + this(cohort, new SessionPeriod(startDate, endDate), new SessionImages(image)); } - public SessionInfo(int cohort, SessionPeriod period, SessionImage coverImage) { + public SessionInfo(int cohort, SessionPeriod period, SessionImages images) { this.cohort = cohort; this.period = period; - this.coverImage = coverImage; + this.images = images; } public int getCohort() { @@ -31,7 +32,11 @@ public LocalDate getEndDate() { return period.getEndDate(); } - public SessionImage getCoverImage() { - return coverImage; + public SessionImage getImage() { + return images.getFirstImage(); + } + + public SessionImages getImages() { + return images; } } diff --git a/src/main/java/nextstep/courses/domain/session/image/SessionImages.java b/src/main/java/nextstep/courses/domain/session/image/SessionImages.java new file mode 100644 index 000000000..b82061c8b --- /dev/null +++ b/src/main/java/nextstep/courses/domain/session/image/SessionImages.java @@ -0,0 +1,32 @@ +package nextstep.courses.domain.session.image; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class SessionImages { + private final List images; + + public SessionImages(SessionImage image) { + this(List.of(image)); + } + + public SessionImages(List images) { + if (images == null || images.isEmpty()) { + throw new IllegalArgumentException("커버 이미지는 최소 1개 이상이어야 합니다."); + } + this.images = new ArrayList<>(images); + } + + public int size() { + return images.size(); + } + + public SessionImage getFirstImage() { + return images.get(0); + } + + public List getImages() { + return Collections.unmodifiableList(images); + } +} diff --git a/src/test/java/nextstep/courses/domain/session/image/SessionImagesTest.java b/src/test/java/nextstep/courses/domain/session/image/SessionImagesTest.java new file mode 100644 index 000000000..ebb579719 --- /dev/null +++ b/src/test/java/nextstep/courses/domain/session/image/SessionImagesTest.java @@ -0,0 +1,40 @@ +package nextstep.courses.domain.session.image; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class SessionImagesTest { + @Test + public void 커버_이미지_목록_생성() { + SessionImage image1 = new SessionImage(100_000L, "png", 300, 200); + SessionImage image2 = new SessionImage(100_000L, "jpg", 300, 200); + + SessionImages images = new SessionImages(Arrays.asList(image1, image2)); + + assertThat(images).isNotNull(); + assertThat(images.size()).isEqualTo(2); + } + + @Test + public void 빈_목록으로_생성_불가() { + assertThatThrownBy(() -> { + new SessionImages(Collections.emptyList()); + }).isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("커버 이미지는 최소 1개 이상이어야 합니다"); + } + + @Test + public void 대표_이미지_조회() { + SessionImage image1 = new SessionImage(100_000L, "png", 300, 200); + SessionImage image2 = new SessionImage(100_000L, "jpg", 300, 200); + + SessionImages images = new SessionImages(Arrays.asList(image1, image2)); + + assertThat(images.getFirstImage()).isEqualTo(image1); + } +} From ff9807b66505930cd4d85c4a577231da7d7f9c1b Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Mon, 15 Dec 2025 01:50:25 +0900 Subject: [PATCH 10/12] =?UTF-8?q?feat:=20=EC=8A=A4=ED=82=A4=EB=A7=88?= =?UTF-8?q?=EC=97=90=20session=5Fcover=5Fimages=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20JdbcSessionRepository=20=EB=8B=A4=EC=A4=91=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=A0=80=EC=9E=A5=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=88=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../courses/domain/session/SessionInfo.java | 4 + .../infrastructure/JdbcSessionRepository.java | 171 +++++++++++------- src/main/resources/schema.sql | 103 ++++++----- 3 files changed, 173 insertions(+), 105 deletions(-) diff --git a/src/main/java/nextstep/courses/domain/session/SessionInfo.java b/src/main/java/nextstep/courses/domain/session/SessionInfo.java index 138a747ca..73eda7ddd 100644 --- a/src/main/java/nextstep/courses/domain/session/SessionInfo.java +++ b/src/main/java/nextstep/courses/domain/session/SessionInfo.java @@ -13,6 +13,10 @@ public class SessionInfo { public SessionInfo(int cohort, LocalDate startDate, LocalDate endDate, SessionImage image) { this(cohort, new SessionPeriod(startDate, endDate), new SessionImages(image)); } + public SessionInfo(int cohort, LocalDate startDate, LocalDate endDate, SessionImages images) { + this(cohort, new SessionPeriod(startDate, endDate), images); + } + public SessionInfo(int cohort, SessionPeriod period, SessionImages images) { this.cohort = cohort; diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java index ac0a02e5d..7379f5712 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java @@ -5,11 +5,12 @@ import nextstep.courses.domain.session.ProgressStatus; import nextstep.courses.domain.session.RecruitmentStatus; import nextstep.courses.domain.session.Session; +import nextstep.courses.domain.session.SessionInfo; import nextstep.courses.domain.session.SessionRepository; -import nextstep.courses.domain.session.SessionStatus; import nextstep.courses.domain.session.SessionType; import nextstep.courses.domain.session.Sessions; import nextstep.courses.domain.session.image.SessionImage; +import nextstep.courses.domain.session.image.SessionImages; import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; @@ -32,60 +33,89 @@ public JdbcSessionRepository(JdbcOperations jdbcTemplate) { @Override public void save(Long courseId, Session session) { + Long sessionId = saveSession(courseId, session); + saveCoverImages(sessionId, session.getImages()); + } + + private Long saveSession(Long courseId, Session session) { Long imageId = saveSessionImage(session.getImage()); + String sql = "insert into session (course_id, cohort, start_date, end_date, image_id, status, progress_status, recruitment_status, session_type, max_capacity, fee, created_at)" + + "values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; - SessionType type = session.getSessionType(); + KeyHolder keyHolder = new GeneratedKeyHolder(); - String sessionTypeStr = type.isFree() ? "FREE" : "PAID"; - Integer maxCapacity = null; - Long fee = null; + jdbcTemplate.update(connection -> { + PreparedStatement ps = connection.prepareStatement(sql, new String[]{"id"}); + ps.setLong(1, courseId); + ps.setInt(2, session.getCohort()); + ps.setDate(3, Date.valueOf(session.getStartDate())); + ps.setDate(4, Date.valueOf(session.getEndDate())); + ps.setLong(5, imageId); + ps.setString(6, getStatusValue(session)); + ps.setString(7, session.getProgressStatus().getValue()); + ps.setString(8, session.getRecruitmentStatus().getValue()); + ps.setString(9, getSessionTypeString(session.getSessionType())); + ps.setObject(10, getMaxCapacity(session.getSessionType()), java.sql.Types.INTEGER); + ps.setObject(11, getFee(session.getSessionType()), java.sql.Types.BIGINT); + ps.setTimestamp(12, Timestamp.valueOf(LocalDateTime.now())); + return ps; + }, keyHolder); + + return Objects.requireNonNull(keyHolder.getKey()).longValue(); + } - if (!type.isFree()) { - PaidSessionType paidType = (PaidSessionType) type; - maxCapacity = paidType.getMaxCapacity(); - fee = paidType.getFee(); + private void saveCoverImages(Long sessionId, SessionImages images) { + for (SessionImage image : images.getImages()) { + Long imageId = saveSessionImage(image); + String sql = "insert into session_cover_images (session_id, session_image_id) values(?, ?)"; + jdbcTemplate.update(sql, sessionId, imageId); } + } - String progressStatusValue = session.getProgressStatus().getValue(); - String recruitmentStatusValue = session.getRecruitmentStatus().getValue(); - String statusValue = session.getRecruitmentStatus().canEnroll() ? "모집중" : session.getProgressStatus().getValue(); + private String getStatusValue(Session session) { + return session.getRecruitmentStatus().canEnroll() ? "모집중" : session.getProgressStatus().getValue(); - String sql = "insert into session (course_id, cohort, start_date, end_date, image_id, status, progress_status, recruitment_status, session_type, max_capacity, fee, created_at)" + - "values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + } - jdbcTemplate.update(sql, - courseId, - session.getCohort(), - Date.valueOf(session.getStartDate()), - Date.valueOf(session.getEndDate()), - imageId, - statusValue, - progressStatusValue, - recruitmentStatusValue, - sessionTypeStr, - maxCapacity, - fee, - Timestamp.valueOf(LocalDateTime.now())); + private String getSessionTypeString(SessionType sessionType) { + return sessionType.isFree() ? "FREE" : "PAID"; + } + + private Integer getMaxCapacity(SessionType sessionType) { + if (sessionType.isFree()) { + return null; + } + return ((PaidSessionType) sessionType).getMaxCapacity(); + } + + private Long getFee(SessionType sessionType) { + if (sessionType.isFree()) { + return null; + } + return ((PaidSessionType) sessionType).getFee(); } @Override public Sessions findByCourseId(Long courseId) { - String sql = "select s.id, s.cohort, s.start_date, s.end_date, s.status, s.progress_status, s.recruitment_status " + + String sql = "select s.id, s.cohort, s.start_date, s.end_date, s.image_id, s.status, s.progress_status, s.recruitment_status " + "s.session_type, s.max_capacity, s.fee, " + - "i.file_size, i.image_type, i.width, i.height " + "from session s " + - "join session_image i on s.image_id = i.id " + "where s.course_id = ? " + "order by s.cohort"; List sessionList = jdbcTemplate.query(sql, (rs, rowNum) -> { - SessionImage image = new SessionImage( - rs.getLong("file_size"), - rs.getString("image_type"), - rs.getInt("width"), - rs.getInt("height")); + + Long sessionId = rs.getLong("id"); + Long imageId = rs.getLong("image_id"); + SessionImages images = findSessionImages(sessionId, imageId); + + SessionInfo info = new SessionInfo( + rs.getInt("cohort"), + rs.getDate("start_date").toLocalDate(), + rs.getDate("end_date").toLocalDate(), + images); SessionType type = "FREE".equals(rs.getString("session_type")) ? new FreeSessionType() @@ -94,15 +124,7 @@ public Sessions findByCourseId(Long courseId) { ProgressStatus progressStatus = ProgressStatus.from(rs.getString("progress_status")); RecruitmentStatus recruitmentStatus = RecruitmentStatus.from(rs.getString("recruitment_status")); - return new Session( - rs.getLong("id"), - rs.getInt("cohort"), - rs.getDate("start_date").toLocalDate(), - rs.getDate("end_date").toLocalDate(), - image, - progressStatus, - recruitmentStatus, - type); + return new Session(info, progressStatus, recruitmentStatus, type); }, courseId); return new Sessions(sessionList); @@ -110,18 +132,20 @@ public Sessions findByCourseId(Long courseId) { @Override public Session findById(Long sessionId) { - String sql = "select s.id, s.cohort, s.start_date, s.end_date, s.status, s.session_type, s.max_capacity, s.fee, " + - "i.file_size, i.image_type, i.width, i.height " + + String sql = "select s.id, s.cohort, s.start_date, s.end_date, s.image_id, s.status, s.session_type, s.max_capacity, s.fee, " + "from session s " + - "join session_image i on s.image_id = i.id " + "where s.id = ?"; return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> { - SessionImage image = new SessionImage( - rs.getLong("file_size"), - rs.getString("image_type"), - rs.getInt("width"), - rs.getInt("height")); + Long id = rs.getLong("id"); + Long imageId = rs.getLong("image_id"); + SessionImages images = findSessionImages(id, imageId); + + SessionInfo info = new SessionInfo( + rs.getInt("cohort"), + rs.getDate("start_date").toLocalDate(), + rs.getDate("end_date").toLocalDate(), + images); ProgressStatus progressStatus = ProgressStatus.from(rs.getString("progress_status")); RecruitmentStatus recruitmentStatus = RecruitmentStatus.from(rs.getString("recruitment_status")); @@ -130,15 +154,7 @@ public Session findById(Long sessionId) { ? new FreeSessionType() : new PaidSessionType(rs.getInt("max_capacity"), rs.getLong("fee")); - return new Session( - rs.getLong("id"), - rs.getInt("cohort"), - rs.getDate("start_date").toLocalDate(), - rs.getDate("end_date").toLocalDate(), - image, - progressStatus, - recruitmentStatus, - type); + return new Session(id, info, progressStatus, recruitmentStatus, type); }, sessionId); } @@ -163,6 +179,39 @@ private Long saveSessionImage(SessionImage image) { return Objects.requireNonNull(keyHolder.getKey()).longValue(); } + private SessionImages findSessionImages(Long sessionId, Long imageId) { + String sql = "select si.file_size, si.image_type, si.width, si.height " + + "from session_cover_images sci " + + "join session_image si on sci.session_image_id = si.id " + + "where sci.session_id = ? " + + "order by sci.id"; + + List imageList = jdbcTemplate.query(sql, + (rs, rowNum) -> new SessionImage( + rs.getLong("file_size"), + rs.getString("image_type"), + rs.getInt("width"), + rs.getInt("height")), + sessionId); + + if (!imageList.isEmpty()) { + return new SessionImages(imageList); + } + + return new SessionImages(findSessionImageById(imageId)); + } + + private SessionImage findSessionImageById(Long imageId) { + String sql = "select file_size, image_type, width, height from session_image where id = ?"; + return jdbcTemplate.queryForObject(sql, + (rs, rowNum) -> new SessionImage( + rs.getLong("file_size"), + rs.getString("image_type"), + rs.getInt("width"), + rs.getInt("height")), + imageId); + } + private Long findSessionImageId(SessionImage image) { String sql = "select id from session_image where file_size = ? and image_type = ? and width = ? and height = ?"; List results = jdbcTemplate.query(sql, diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index a19ff21e2..3d8854681 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -1,50 +1,55 @@ -create table course ( - id bigint generated by default as identity, - title varchar(255) not null, - creator_id bigint not null, - created_at timestamp not null, +create table course +( + id bigint generated by default as identity, + title varchar(255) not null, + creator_id bigint not null, + created_at timestamp not null, updated_at timestamp, primary key (id) ); -create table ns_user ( - id bigint generated by default as identity, - user_id varchar(20) not null, - password varchar(20) not null, - name varchar(20) not null, - email varchar(50), - created_at timestamp not null, +create table ns_user +( + id bigint generated by default as identity, + user_id varchar(20) not null, + password varchar(20) not null, + name varchar(20) not null, + email varchar(50), + created_at timestamp not null, updated_at timestamp, primary key (id) ); -create table question ( - id bigint generated by default as identity, - created_at timestamp not null, +create table question +( + id bigint generated by default as identity, + created_at timestamp not null, updated_at timestamp, - contents clob, - deleted boolean not null, - title varchar(100) not null, - writer_id bigint, + contents clob, + deleted boolean not null, + title varchar(100) not null, + writer_id bigint, primary key (id) ); -create table answer ( - id bigint generated by default as identity, - created_at timestamp not null, - updated_at timestamp, - contents clob, - deleted boolean not null, +create table answer +( + id bigint generated by default as identity, + created_at timestamp not null, + updated_at timestamp, + contents clob, + deleted boolean not null, question_id bigint, - writer_id bigint, + writer_id bigint, primary key (id) ); -create table delete_history ( - id bigint not null, - content_id bigint, - content_type varchar(255), - created_date timestamp, +create table delete_history +( + id bigint not null, + content_id bigint, + content_type varchar(255), + created_date timestamp, deleted_by_id bigint, primary key (id) ); @@ -63,20 +68,20 @@ create table session_image create table session ( - id bigint generated by default as identity, - course_id bigint not null, - cohort int not null, - start_date date not null, - end_date date not null, - image_id bigint not null, - status varchar(20) not null, - progress_status varchar(20), + id bigint generated by default as identity, + course_id bigint not null, + cohort int not null, + start_date date not null, + end_date date not null, + image_id bigint not null, + status varchar(20) not null, + progress_status varchar(20), recruitment_status varchar(20), - session_type varchar(20) not null, - max_capacity int, - fee bigint, - created_at timestamp not null, - updated_at timestamp, + session_type varchar(20) not null, + max_capacity int, + fee bigint, + created_at timestamp not null, + updated_at timestamp, primary key (id), foreign key (course_id) references course (id), foreign key (image_id) references session_image (id) @@ -93,3 +98,13 @@ create table session_enrollment foreign key (ns_user_id) references ns_user (id) ); + +create table session_cover_images +( + id bigint generated by default as identity, + session_id bigint not null, + session_image_id bigint not null, + primary key (id), + foreign key (session_id) references session (id), + foreign key (session_image_id) references session_image (id) +); From f0deb6ea51341693783a629e87c26ce0dc6c4e98 Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Mon, 15 Dec 2025 23:55:08 +0900 Subject: [PATCH 11/12] =?UTF-8?q?feat:=20=EC=A1=B0=ED=9A=8C=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=EC=97=90=EC=84=9C=20status=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../courses/infrastructure/JdbcSessionRepository.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java index 7379f5712..e6c5f686d 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java @@ -99,7 +99,7 @@ private Long getFee(SessionType sessionType) { @Override public Sessions findByCourseId(Long courseId) { - String sql = "select s.id, s.cohort, s.start_date, s.end_date, s.image_id, s.status, s.progress_status, s.recruitment_status " + + String sql = "select s.id, s.cohort, s.start_date, s.end_date, s.image_id, s.progress_status, s.recruitment_status " + "s.session_type, s.max_capacity, s.fee, " + "from session s " + "where s.course_id = ? " + @@ -132,7 +132,7 @@ public Sessions findByCourseId(Long courseId) { @Override public Session findById(Long sessionId) { - String sql = "select s.id, s.cohort, s.start_date, s.end_date, s.image_id, s.status, s.session_type, s.max_capacity, s.fee, " + + String sql = "select s.id, s.cohort, s.start_date, s.end_date, s.image_id, s.session_type, s.max_capacity, s.fee, " + "from session s " + "where s.id = ?"; From 8a65f0fed4de5db83180e98311e8aa5975d6ebc8 Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Tue, 16 Dec 2025 01:42:37 +0900 Subject: [PATCH 12/12] =?UTF-8?q?feat:=20=EC=88=98=EA=B0=95=EC=8B=A0?= =?UTF-8?q?=EC=B2=AD=EC=84=9C=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=98?= =?UTF-8?q?=EC=97=AC=20=EC=8A=B9=EC=9D=B8=EB=90=9C=20=ED=95=99=EC=83=9D?= =?UTF-8?q?=EB=A7=8C=20=EC=88=98=EA=B0=95=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../courses/domain/session/Enrollment.java | 19 ++++++ .../domain/session/EnrollmentApplication.java | 67 +++++++++++++++++++ .../domain/session/EnrollmentRepository.java | 5 +- .../domain/session/EnrollmentStatus.java | 34 ++++++++++ .../courses/domain/session/SessionType.java | 9 +-- .../JdbcEnrollmentRepository.java | 28 ++++++++ .../courses/service/SessionService.java | 24 +++++++ src/main/resources/schema.sql | 3 + .../session/EnrollmentApplicationTest.java | 59 ++++++++++++++++ .../domain/session/EnrollmentStatusTest.java | 45 +++++++++++++ .../domain/session/EnrollmentTest.java | 22 ++++++ 11 files changed, 308 insertions(+), 7 deletions(-) create mode 100644 src/main/java/nextstep/courses/domain/session/EnrollmentApplication.java create mode 100644 src/main/java/nextstep/courses/domain/session/EnrollmentStatus.java create mode 100644 src/test/java/nextstep/courses/domain/session/EnrollmentApplicationTest.java create mode 100644 src/test/java/nextstep/courses/domain/session/EnrollmentStatusTest.java diff --git a/src/main/java/nextstep/courses/domain/session/Enrollment.java b/src/main/java/nextstep/courses/domain/session/Enrollment.java index 6f3c0fe75..ea6c45d6a 100644 --- a/src/main/java/nextstep/courses/domain/session/Enrollment.java +++ b/src/main/java/nextstep/courses/domain/session/Enrollment.java @@ -37,6 +37,25 @@ public EnrolledStudent enroll(Long nsUserId, Payment payment) { return new EnrolledStudent(sessionId, nsUserId); } + public EnrollmentApplication apply(Long nsUserId, Payment payment) { + if (!recruitmentStatus.canEnroll()) { + throw new IllegalStateException("모집중인 강의만 수강 신청할 수 있습니다."); + } + if (!sessionType.isValidPayment(payment)) { + throw new IllegalArgumentException("결제 금액이 수강료와 일치하지 않습니다."); + } + if (sessionType.isOverCapacity(enrolledStudents.size())) { + throw new IllegalStateException("최대 수강 인원을 초과했습니다."); + } + return new EnrollmentApplication(sessionId, nsUserId, payment); + } + + public EnrolledStudent approve(EnrollmentApplication application, Long adminId) { + application.approve(adminId); + return new EnrolledStudent(application.getSessionId(), application.getNsUserId()); + } + + public SessionType getSessionType() { return sessionType; } diff --git a/src/main/java/nextstep/courses/domain/session/EnrollmentApplication.java b/src/main/java/nextstep/courses/domain/session/EnrollmentApplication.java new file mode 100644 index 000000000..9cf81105b --- /dev/null +++ b/src/main/java/nextstep/courses/domain/session/EnrollmentApplication.java @@ -0,0 +1,67 @@ +package nextstep.courses.domain.session; + +import nextstep.payments.domain.Payment; + +import java.time.LocalDateTime; + +public class EnrollmentApplication { + private final Long sessionId; + private final Long nsUserId; + private final Payment payment; + private EnrollmentStatus status; + private LocalDateTime approvedAt; + private Long approvedBy; + + public EnrollmentApplication(Long sessionId, Long nsUserId, Payment payment) { + this(sessionId, nsUserId, payment, EnrollmentStatus.PENDING, LocalDateTime.now(), null); + } + + public EnrollmentApplication(Long sessionId, Long nsUserId, Payment payment, EnrollmentStatus status, LocalDateTime approvedAt, Long approvedBy) { + this.sessionId = sessionId; + this.nsUserId = nsUserId; + this.payment = payment; + this.status = status; + this.approvedAt = approvedAt; + this.approvedBy = approvedBy; + } + + public void approve(Long adminId) { + if (!status.isPending()) { + throw new IllegalStateException("대기 중인 신청만 승인 가능합니다."); + } + this.status = EnrollmentStatus.APPROVED; + this.approvedAt = LocalDateTime.now(); + this.approvedBy = adminId; + } + + public void cancel() { + if (!status.isPending()) { + throw new IllegalStateException("대기 중인 신청만 취소 가능합니다."); + } + this.status = EnrollmentStatus.CANCELLED; + } + + public Long getSessionId() { + return sessionId; + } + + public Long getNsUserId() { + return nsUserId; + } + + public Payment getPayment() { + return payment; + } + + public EnrollmentStatus getStatus() { + return status; + } + + public LocalDateTime getApprovedAt() { + return approvedAt; + } + + public Long getApprovedBy() { + return approvedBy; + } +} diff --git a/src/main/java/nextstep/courses/domain/session/EnrollmentRepository.java b/src/main/java/nextstep/courses/domain/session/EnrollmentRepository.java index b0d5eda83..9c476e72d 100644 --- a/src/main/java/nextstep/courses/domain/session/EnrollmentRepository.java +++ b/src/main/java/nextstep/courses/domain/session/EnrollmentRepository.java @@ -3,8 +3,11 @@ import java.util.List; public interface EnrollmentRepository { - void save (EnrolledStudent enrolledStudent); + void save(EnrolledStudent enrolledStudent); List findBySessionId(Long sessionId); + void saveApplication(EnrollmentApplication application); + + void updateApplication(EnrollmentApplication application); } diff --git a/src/main/java/nextstep/courses/domain/session/EnrollmentStatus.java b/src/main/java/nextstep/courses/domain/session/EnrollmentStatus.java new file mode 100644 index 000000000..9f4ca5842 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/session/EnrollmentStatus.java @@ -0,0 +1,34 @@ +package nextstep.courses.domain.session; + +import java.util.Arrays; + +public enum EnrollmentStatus { + PENDING("대기중"), + APPROVED("승인됨"), + CANCELLED("취소됨"); + + private final String value; + + EnrollmentStatus(String value) { + this.value = value; + } + + public static EnrollmentStatus from(String description) { + return Arrays.stream(values()) + .filter(status -> status.value.equals(description)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("잘못된 수강신청 상태입니다: " + description)); + } + + public boolean isApproved() { + return this == APPROVED; + } + + public boolean isPending() { + return this == PENDING; + } + + public String getValue() { + return value; + } +} diff --git a/src/main/java/nextstep/courses/domain/session/SessionType.java b/src/main/java/nextstep/courses/domain/session/SessionType.java index 16c025049..a6e1aabd0 100644 --- a/src/main/java/nextstep/courses/domain/session/SessionType.java +++ b/src/main/java/nextstep/courses/domain/session/SessionType.java @@ -3,10 +3,7 @@ import nextstep.payments.domain.Payment; public interface SessionType { - - public boolean isFree(); - - public boolean isOverCapacity(int currentEnrollmentCount); - - public boolean isValidPayment(Payment payment); + public boolean isFree(); + public boolean isOverCapacity(int currentEnrollmentCount); + public boolean isValidPayment(Payment payment); } diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java index c91173cfd..f947ad4cf 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java @@ -1,6 +1,7 @@ package nextstep.courses.infrastructure; import nextstep.courses.domain.session.EnrolledStudent; +import nextstep.courses.domain.session.EnrollmentApplication; import nextstep.courses.domain.session.EnrollmentRepository; import org.springframework.jdbc.core.JdbcOperations; import org.springframework.stereotype.Repository; @@ -32,4 +33,31 @@ public List findBySessionId(Long sessionId) { rs.getLong("ns_user_id") ), sessionId); } + + @Override + public void saveApplication(EnrollmentApplication application) { + String sql = "insert into session_enrollment (session_id, ns_user_id, enrolled_at, enrollment_status) values(?, ?, ?, ?)"; + jdbcTemplate.update(sql, + application.getSessionId(), + application.getNsUserId(), + Timestamp.valueOf(LocalDateTime.now()), + application.getStatus().getValue()); + } + + @Override + public void updateApplication(EnrollmentApplication application) { + String sql = "update session_enrollment set enrollment_status = ?, approved_at = ?, approved_by = ? " + + "where session_id = ? and ns_user_id = ?"; + + Timestamp approvedAt = application.getApprovedAt() != null + ? Timestamp.valueOf(application.getApprovedAt()) + : null; + + jdbcTemplate.update(sql, + application.getStatus().getValue(), + approvedAt, + application.getApprovedBy(), + application.getSessionId(), + application.getNsUserId()); + } } diff --git a/src/main/java/nextstep/courses/service/SessionService.java b/src/main/java/nextstep/courses/service/SessionService.java index f7a3799a7..e2c354ac6 100644 --- a/src/main/java/nextstep/courses/service/SessionService.java +++ b/src/main/java/nextstep/courses/service/SessionService.java @@ -2,6 +2,7 @@ import nextstep.courses.domain.session.EnrolledStudent; import nextstep.courses.domain.session.Enrollment; +import nextstep.courses.domain.session.EnrollmentApplication; import nextstep.courses.domain.session.EnrollmentRepository; import nextstep.courses.domain.session.Session; import nextstep.courses.domain.session.SessionRepository; @@ -30,4 +31,27 @@ public void enroll(Long sessionId, Long nsUserId, Payment payment) { enrollmentRepository.save(enroll); } + + public void applyForEnrollment(Long sessionId, Long nsUserId, Payment payment) { + Session session = sessionRepository.findById(sessionId); + List students = enrollmentRepository.findBySessionId(sessionId); + + Enrollment enrollment = session.createEnrollment(students); + EnrollmentApplication application = enrollment.apply(nsUserId, payment); + + enrollmentRepository.saveApplication(application); + } + + public void approveEnrollment(Long sessionId, Long nsUserId, Long instructorId) { + Session session = sessionRepository.findById(sessionId); + List students = enrollmentRepository.findBySessionId(sessionId); + + Enrollment enrollment = session.createEnrollment(students); + EnrollmentApplication application = new EnrollmentApplication(sessionId, nsUserId, null); + EnrolledStudent student = enrollment.approve(application, instructorId); + + enrollmentRepository.updateApplication(application); + enrollmentRepository.save(student); + } + } diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 3d8854681..122dd15aa 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -93,6 +93,9 @@ create table session_enrollment session_id bigint not null, ns_user_id bigint not null, enrolled_at timestamp not null, + enrollment_status varchar(20), + approved_at timestamp, + approved_by bigint, primary key (id), foreign key (session_id) references session (id), foreign key (ns_user_id) references ns_user (id) diff --git a/src/test/java/nextstep/courses/domain/session/EnrollmentApplicationTest.java b/src/test/java/nextstep/courses/domain/session/EnrollmentApplicationTest.java new file mode 100644 index 000000000..9781c35ce --- /dev/null +++ b/src/test/java/nextstep/courses/domain/session/EnrollmentApplicationTest.java @@ -0,0 +1,59 @@ +package nextstep.courses.domain.session; + +import nextstep.payments.domain.Payment; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class EnrollmentApplicationTest { + @Test + public void 신청서를_생성한다() { + Payment payment = new Payment("결제번호-1", 1L, 1L, 50_000L); + + EnrollmentApplication application = new EnrollmentApplication(1L, 1L, payment); + + assertThat(application.getSessionId()).isEqualTo(1L); + assertThat(application.getNsUserId()).isEqualTo(1L); + assertThat(application.getStatus()).isEqualTo(EnrollmentStatus.PENDING); + } + + @Test + public void 신청서를_승인한다() { + EnrollmentApplication application = new EnrollmentApplication(1L, 1L, null); + + application.approve(100L); + + assertThat(application.getStatus()).isEqualTo(EnrollmentStatus.APPROVED); + assertThat(application.getApprovedBy()).isEqualTo(100L); + } + + @Test + public void 신청서를_취소한다() { + EnrollmentApplication application = new EnrollmentApplication(1L, 1L, null); + + application.cancel(); + + assertThat(application.getStatus()).isEqualTo(EnrollmentStatus.CANCELLED); + } + + @Test + public void 이미_승인된_신청서는_다시_승인_불가() { + EnrollmentApplication application = new EnrollmentApplication(1L, 1L, null); + application.approve(100L); + + assertThatThrownBy(() -> application.approve(100L)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("대기 중인 신청만 승인 가능합니다"); + } + + @Test + public void 이미_취소된_신청서는_승인_불가() { + EnrollmentApplication application = new EnrollmentApplication(1L, 1L, null); + application.cancel(); + + assertThatThrownBy(() -> application.approve(100L)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("대기 중인 신청만 승인 가능합니다"); + } +} diff --git a/src/test/java/nextstep/courses/domain/session/EnrollmentStatusTest.java b/src/test/java/nextstep/courses/domain/session/EnrollmentStatusTest.java new file mode 100644 index 000000000..1cddd08a4 --- /dev/null +++ b/src/test/java/nextstep/courses/domain/session/EnrollmentStatusTest.java @@ -0,0 +1,45 @@ +package nextstep.courses.domain.session; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class EnrollmentStatusTest { + + @Test + public void 설명으로_상태_조회한다() { + EnrollmentStatus pending = EnrollmentStatus.from("대기중"); + + assertThat(pending).isEqualTo(EnrollmentStatus.PENDING); + } + + @Test + public void 잘못된_설명으로_조회시_예외발생() { + assertThatThrownBy(() -> EnrollmentStatus.from("잘못된상태")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("잘못된 수강신청 상태입니다"); + } + + @ParameterizedTest + @CsvSource({ + "APPROVED, true", + "PENDING, false", + "CANCELLED, false" + }) + public void 승인_여부를_확인한다(EnrollmentStatus status, boolean isApproved) { + assertThat(status.isApproved()).isEqualTo(isApproved); + } + + @ParameterizedTest + @CsvSource({ + "PENDING, true", + "APPROVED, false", + "CANCELLED, false" + }) + public void 대기중_상태를_확인한다(EnrollmentStatus status, boolean isPending) { + assertThat(status.isPending()).isEqualTo(isPending); + } +} diff --git a/src/test/java/nextstep/courses/domain/session/EnrollmentTest.java b/src/test/java/nextstep/courses/domain/session/EnrollmentTest.java index 438e25af0..57db351cf 100644 --- a/src/test/java/nextstep/courses/domain/session/EnrollmentTest.java +++ b/src/test/java/nextstep/courses/domain/session/EnrollmentTest.java @@ -58,4 +58,26 @@ public class EnrollmentTest { .hasMessageContaining("최대 수강 인원을 초과"); } + @Test + public void 신청서_생성() { + Enrollment enrollment = new Enrollment(1L, RecruitmentStatus.RECRUITING, new FreeSessionType(), Collections.emptyList()); + + EnrollmentApplication application = enrollment.apply(1L, null); + + assertThat(application.getSessionId()).isEqualTo(1L); + assertThat(application.getNsUserId()).isEqualTo(1L); + assertThat(application.getStatus()).isEqualTo(EnrollmentStatus.PENDING); + } + + @Test + public void 신청서_승인하여_수강생_등록() { + Enrollment enrollment = new Enrollment(1L, RecruitmentStatus.RECRUITING, new FreeSessionType(), Collections.emptyList()); + EnrollmentApplication application = enrollment.apply(1L, null); + + EnrolledStudent student = enrollment.approve(application, 100L); + + assertThat(student.getSessionId()).isEqualTo(1L); + assertThat(student.getNsUserId()).isEqualTo(1L); + assertThat(application.getStatus()).isEqualTo(EnrollmentStatus.APPROVED); + } }