From 5eecd3689d85d3d756765a213bfb07815ee3aed4 Mon Sep 17 00:00:00 2001 From: ho-jun97 Date: Tue, 16 Dec 2025 01:20:45 +0900 Subject: [PATCH 1/6] =?UTF-8?q?refactor:=20Session=20Test=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20Builder=20=EC=83=9D=EC=84=B1=20=EB=B0=8F?= =?UTF-8?q?=20=ED=94=BC=EB=93=9C=EB=B0=B1=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/courses/domain/Enrollment.java | 21 +--- .../courses/domain/EnrollmentRule.java | 2 +- .../courses/domain/FreeEnrollmentRule.java | 7 +- .../java/nextstep/courses/domain/Money.java | 6 +- .../courses/domain/PaidEnrollmentRule.java | 4 +- .../java/nextstep/courses/domain/Session.java | 7 +- .../domain/PaidEnrollmentRuleTest.java | 2 +- .../courses/domain/SessionBuilder.java | 42 ++++++++ .../nextstep/courses/domain/SessionTest.java | 96 +++++++------------ 9 files changed, 99 insertions(+), 88 deletions(-) create mode 100644 src/test/java/nextstep/courses/domain/SessionBuilder.java diff --git a/src/main/java/nextstep/courses/domain/Enrollment.java b/src/main/java/nextstep/courses/domain/Enrollment.java index 9c261962a0..ec691803a0 100644 --- a/src/main/java/nextstep/courses/domain/Enrollment.java +++ b/src/main/java/nextstep/courses/domain/Enrollment.java @@ -5,28 +5,18 @@ public class Enrollment { private final Long studentId; private final Long sessionId; - private final Money money; - public Enrollment(Long studentId, Long sessionId, int money) { - this(studentId, sessionId, new Money(money)); - } - - public Enrollment(Long studentId, Long sessionId, Money money) { + public Enrollment(Long studentId, Long sessionId) { this.studentId = studentId; this.sessionId = sessionId; - this.money = money; - } - - public Money getMoney() { - return money; } public boolean sameSession(Long id) { return this.sessionId.equals(id); } - public void validateBelongsTo(Session session) { - if (!sameSession(session.getId())) { + public void validateBelongsTo(Long sessionId) { + if (!sameSession(sessionId)) { throw new IllegalArgumentException("신청하고자 하는 강의가 아닙니다."); } } @@ -35,12 +25,12 @@ public void validateBelongsTo(Session session) { public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; Enrollment that = (Enrollment) o; - return Objects.equals(studentId, that.studentId) && Objects.equals(sessionId, that.sessionId) && Objects.equals(money, that.money); + return Objects.equals(studentId, that.studentId) && Objects.equals(sessionId, that.sessionId); } @Override public int hashCode() { - return Objects.hash(studentId, sessionId, money); + return Objects.hash(studentId, sessionId); } @Override @@ -48,7 +38,6 @@ public String toString() { return "Enrollment{" + "studentId=" + studentId + ", sessionId=" + sessionId + - ", money=" + money + '}'; } } diff --git a/src/main/java/nextstep/courses/domain/EnrollmentRule.java b/src/main/java/nextstep/courses/domain/EnrollmentRule.java index 388c7712ad..d2399ad61c 100644 --- a/src/main/java/nextstep/courses/domain/EnrollmentRule.java +++ b/src/main/java/nextstep/courses/domain/EnrollmentRule.java @@ -1,7 +1,7 @@ package nextstep.courses.domain; public interface EnrollmentRule { - void validate(int money, int enrolledCount); + void validate(Money money, int enrolledCount); void validateMoney(Money money); diff --git a/src/main/java/nextstep/courses/domain/FreeEnrollmentRule.java b/src/main/java/nextstep/courses/domain/FreeEnrollmentRule.java index e5a7d51c87..a97102702f 100644 --- a/src/main/java/nextstep/courses/domain/FreeEnrollmentRule.java +++ b/src/main/java/nextstep/courses/domain/FreeEnrollmentRule.java @@ -1,8 +1,13 @@ package nextstep.courses.domain; public class FreeEnrollmentRule implements EnrollmentRule { + + public FreeEnrollmentRule() { + + } + @Override - public void validate(int money, int enrolledCount) { + public void validate(Money money, int enrolledCount) { // 무료 강의는 아무 제한 없음 } diff --git a/src/main/java/nextstep/courses/domain/Money.java b/src/main/java/nextstep/courses/domain/Money.java index 568a89f7d9..7edb8b6209 100644 --- a/src/main/java/nextstep/courses/domain/Money.java +++ b/src/main/java/nextstep/courses/domain/Money.java @@ -3,7 +3,9 @@ import java.util.Objects; public class Money { - private int money; + public static final Money ZERO = new Money(0); + + private final int money; public Money(int money) { validation(money); @@ -21,7 +23,7 @@ public boolean matches(int money) { } public boolean matches(Money money) { - return this.money == money.getMoney(); + return matches(money.getMoney()); } public int getMoney() { diff --git a/src/main/java/nextstep/courses/domain/PaidEnrollmentRule.java b/src/main/java/nextstep/courses/domain/PaidEnrollmentRule.java index a944d48bdc..8294a63f8b 100644 --- a/src/main/java/nextstep/courses/domain/PaidEnrollmentRule.java +++ b/src/main/java/nextstep/courses/domain/PaidEnrollmentRule.java @@ -14,8 +14,8 @@ public class PaidEnrollmentRule implements EnrollmentRule { } @Override - public void validate(int money, int enrolledCount) { - validateMoney(new Money(money)); + public void validate(Money money, int enrolledCount) { + validateMoney(money); validateCapacity(enrolledCount); } diff --git a/src/main/java/nextstep/courses/domain/Session.java b/src/main/java/nextstep/courses/domain/Session.java index bf22d963a6..1be402d29d 100644 --- a/src/main/java/nextstep/courses/domain/Session.java +++ b/src/main/java/nextstep/courses/domain/Session.java @@ -25,13 +25,12 @@ public int countEnrollments() { return enrollments.countEnrollments(); } - public void enroll(Enrollment enrollment) { + public void enroll(Enrollment enrollment, Money money) { validationRecruiting(); - enrollment.validateBelongsTo(this); + enrollment.validateBelongsTo(getId()); - enrollmentRule.validateMoney(enrollment.getMoney()); - enrollmentRule.validateCapacity(enrollments.countEnrollments()); + enrollmentRule.validate(money, countEnrollments()); enrollments.enroll(enrollment); } diff --git a/src/test/java/nextstep/courses/domain/PaidEnrollmentRuleTest.java b/src/test/java/nextstep/courses/domain/PaidEnrollmentRuleTest.java index 170f36bcb3..f0768196bc 100644 --- a/src/test/java/nextstep/courses/domain/PaidEnrollmentRuleTest.java +++ b/src/test/java/nextstep/courses/domain/PaidEnrollmentRuleTest.java @@ -8,6 +8,6 @@ public class PaidEnrollmentRuleTest { void 유료_강의_등록_성공() { PaidEnrollmentRule paidEnrollmentRule = new PaidEnrollmentRule(50000, 5); - paidEnrollmentRule.validate(50000,1); + paidEnrollmentRule.validate(new Money(50000),1); } } diff --git a/src/test/java/nextstep/courses/domain/SessionBuilder.java b/src/test/java/nextstep/courses/domain/SessionBuilder.java new file mode 100644 index 0000000000..92f7d4fe23 --- /dev/null +++ b/src/test/java/nextstep/courses/domain/SessionBuilder.java @@ -0,0 +1,42 @@ +package nextstep.courses.domain; + +import java.time.LocalDateTime; + +public class SessionBuilder { + private Long id = 1L; + private ImageFile imageFile = new ImageFile(1024 * 1024); + private SessionPeriod period = + new SessionPeriod(LocalDateTime.now(), LocalDateTime.now().plusDays(7)); + private SessionStatus sessionStatus = SessionStatus.RECRUITING; + private EnrollmentRule enrollmentRule = new PaidEnrollmentRule(50000, 10); + private Enrollments enrollments = new Enrollments(); + + public static SessionBuilder aRecuitingSession() { + return new SessionBuilder(); + } + + public static SessionBuilder anEndSession() { + return new SessionBuilder() + .withStatus(SessionStatus.END); + } + + public SessionBuilder withStatus(SessionStatus status) { + this.sessionStatus = status; + return this; + } + + public SessionBuilder asPaidSession(int price, int capacity) { + this.enrollmentRule = new PaidEnrollmentRule(price, capacity); + return this; + } + + public SessionBuilder asFreeSession() { + this.enrollmentRule = new FreeEnrollmentRule(); + return this; + } + + public Session build() { + return new Session(id, imageFile, period, sessionStatus, enrollmentRule, enrollments); + } + +} diff --git a/src/test/java/nextstep/courses/domain/SessionTest.java b/src/test/java/nextstep/courses/domain/SessionTest.java index 3930d4c6d4..f709a40bd4 100644 --- a/src/test/java/nextstep/courses/domain/SessionTest.java +++ b/src/test/java/nextstep/courses/domain/SessionTest.java @@ -1,52 +1,42 @@ package nextstep.courses.domain; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.time.LocalDateTime; - import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; public class SessionTest { - ImageFile imageFile; - SessionPeriod period; + @Test + void 무료_강의_등록() { + Session session = SessionBuilder.aRecuitingSession() + .asFreeSession() + .build(); - @BeforeEach - void setUp() { - imageFile = new ImageFile(1024 * 1024); - period = new SessionPeriod(LocalDateTime.now(), LocalDateTime.now().plusDays(7)); - } + session.enroll(new Enrollment(1L, 1L), Money.ZERO); + assertThat(session.countEnrollments()).isEqualTo(1); + } @Test void 강의_등록_성공() { - Session session = new Session( - 1L, - imageFile, - period, - SessionStatus.RECRUITING, - new PaidEnrollmentRule(50000, 10) - ); + Session session = SessionBuilder.aRecuitingSession() + .asPaidSession(50000, 10) + .build(); - session.enroll(new Enrollment(1L, 1L, 50000)); + session.enroll(new Enrollment(1L, 1L), new Money(50000)); assertThat(session.countEnrollments()).isEqualTo(1); } @Test void 강의_금액_불일치_예외() { - Session session = new Session( - 1L, - imageFile, - period, - SessionStatus.RECRUITING, - new PaidEnrollmentRule(50000, 10) - ); + Session session = SessionBuilder.aRecuitingSession() + .asPaidSession(50000, 10) + .build(); assertThatThrownBy( - () -> session.enroll(new Enrollment(1L, 1L, 45000)) + () -> session.enroll(new Enrollment(1L, 1L), new Money(45000)) ).isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("결제 금액이 수강료와 일치하지 않습니다."); @@ -54,68 +44,52 @@ void setUp() { @Test void 마감된_강의_등록시_예외() { - Session session = new Session( - 1L, - imageFile, - period, - SessionStatus.END, - new PaidEnrollmentRule(50000, 10) - ); + Session session = SessionBuilder.anEndSession() + .asPaidSession(50000, 10) + .build(); assertThatThrownBy( - () -> session.enroll(new Enrollment(1L, 1L, 50000)) + () -> session.enroll(new Enrollment(1L, 1L), new Money(50000)) ).isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("모집중인 강의만 수강 신청할 수 있습니다."); } @Test void 정원_초과_등록시_예외() { - Session session = new Session( - 1L, - imageFile, - period, - SessionStatus.RECRUITING, - new PaidEnrollmentRule(50000, 1) - ); - session.enroll(new Enrollment(1L, 1L, 50000)); + Session session = SessionBuilder.aRecuitingSession() + .asPaidSession(50000, 1) + .build(); + + session.enroll(new Enrollment(1L, 1L), new Money(50000)); assertThatThrownBy( - () -> session.enroll(new Enrollment(2L, 1L, 50000)) + () -> session.enroll(new Enrollment(2L, 1L), new Money(50000)) ).isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("수강 인원이 초과되었습니다."); } @Test void 이미_수강_중인_강의_등록시_예외() { - Session session = new Session( - 1L, - imageFile, - period, - SessionStatus.RECRUITING, - new PaidEnrollmentRule(50000, 10) - ); + Session session = SessionBuilder.aRecuitingSession() + .asPaidSession(50000, 2) + .build(); - session.enroll(new Enrollment(1L, 1L, 50000)); + session.enroll(new Enrollment(1L, 1L), new Money(50000)); assertThatThrownBy( - () -> session.enroll(new Enrollment(1L, 1L, 50000)) + () -> session.enroll(new Enrollment(1L, 1L), new Money(50000)) ).isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("이미 수강 신청한 강의입니다."); } @Test void 신청한_강의와_결제한_강의와_다를_경우_예외() { - Session session = new Session( - 1L, - imageFile, - period, - SessionStatus.RECRUITING, - new PaidEnrollmentRule(50000, 10) - ); - + Session session = SessionBuilder.aRecuitingSession() + .asPaidSession(50000, 2) + .build(); assertThatThrownBy( - () -> session.enroll(new Enrollment(1L, 2L, 50000)) + () -> session.enroll(new Enrollment(1L, 2L), new Money(50000)) ).isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("신청하고자 하는 강의가 아닙니다."); } From e256a387246df191b92d5199bea7f9c0a3a54269 Mon Sep 17 00:00:00 2001 From: ho-jun97 Date: Mon, 22 Dec 2025 23:15:49 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20DB=20=ED=85=8C=EC=9D=B4=EB=B8=94=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20CRUD=20=EC=BD=94=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/courses/domain/Capacity.java | 4 + .../nextstep/courses/domain/Enrollment.java | 23 +++++- .../courses/domain/EnrollmentRule.java | 4 + .../courses/domain/FreeEnrollmentRule.java | 10 +++ .../nextstep/courses/domain/ImageFile.java | 64 +++++++++++++-- .../courses/domain/PaidEnrollmentRule.java | 14 +++- .../java/nextstep/courses/domain/Session.java | 59 +++++++++++++- .../courses/domain/SessionPeriod.java | 29 +++++++ .../infrastructure/JdbcCourseRepository.java | 2 +- .../JdbcEnrollmentRepository.java | 35 ++++++++ .../JdbcImageFileRepository.java | 44 ++++++++++ .../infrastructure/JdbcSessionRepository.java | 80 +++++++++++++++++++ .../CourseRepository.java | 4 +- .../repository/EnrollmentRepository.java | 9 +++ .../repository/ImageFileRepository.java | 9 +++ .../courses/repository/SessionRepository.java | 9 +++ src/main/resources/schema.sql | 27 +++++++ .../infrastructure/CourseRepositoryTest.java | 2 +- .../repository/EnrollmentRepositoryTest.java | 34 ++++++++ .../repository/ImageFileRepositoryTest.java | 34 ++++++++ .../repository/SessionRepositoryTest.java | 55 +++++++++++++ 21 files changed, 535 insertions(+), 16 deletions(-) create mode 100644 src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java create mode 100644 src/main/java/nextstep/courses/infrastructure/JdbcImageFileRepository.java create mode 100644 src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java rename src/main/java/nextstep/courses/{domain => repository}/CourseRepository.java (56%) create mode 100644 src/main/java/nextstep/courses/repository/EnrollmentRepository.java create mode 100644 src/main/java/nextstep/courses/repository/ImageFileRepository.java create mode 100644 src/main/java/nextstep/courses/repository/SessionRepository.java create mode 100644 src/test/java/nextstep/courses/repository/EnrollmentRepositoryTest.java create mode 100644 src/test/java/nextstep/courses/repository/ImageFileRepositoryTest.java create mode 100644 src/test/java/nextstep/courses/repository/SessionRepositoryTest.java diff --git a/src/main/java/nextstep/courses/domain/Capacity.java b/src/main/java/nextstep/courses/domain/Capacity.java index 57778f334b..25479c4e0d 100644 --- a/src/main/java/nextstep/courses/domain/Capacity.java +++ b/src/main/java/nextstep/courses/domain/Capacity.java @@ -17,4 +17,8 @@ private void validation(int capacity) { public boolean isFull(int enrolledCount) { return capacity <= enrolledCount; } + + public int getCapacity() { + return capacity; + } } diff --git a/src/main/java/nextstep/courses/domain/Enrollment.java b/src/main/java/nextstep/courses/domain/Enrollment.java index ec691803a0..1278be86ee 100644 --- a/src/main/java/nextstep/courses/domain/Enrollment.java +++ b/src/main/java/nextstep/courses/domain/Enrollment.java @@ -3,10 +3,16 @@ import java.util.Objects; public class Enrollment { + private final Long id; private final Long studentId; private final Long sessionId; - public Enrollment(Long studentId, Long sessionId) { + public Enrollment(Long studentId, Long sessionId) { + this(1L, studentId, sessionId); + } + + public Enrollment(Long id, Long studentId, Long sessionId) { + this.id = id; this.studentId = studentId; this.sessionId = sessionId; } @@ -21,22 +27,31 @@ public void validateBelongsTo(Long sessionId) { } } + public Long getStudentId() { + return studentId; + } + + public Long getSessionId() { + return sessionId; + } + @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; Enrollment that = (Enrollment) o; - return Objects.equals(studentId, that.studentId) && Objects.equals(sessionId, that.sessionId); + return Objects.equals(id, that.id) && Objects.equals(studentId, that.studentId) && Objects.equals(sessionId, that.sessionId); } @Override public int hashCode() { - return Objects.hash(studentId, sessionId); + return Objects.hash(id, studentId, sessionId); } @Override public String toString() { return "Enrollment{" + - "studentId=" + studentId + + "id=" + id + + ", studentId=" + studentId + ", sessionId=" + sessionId + '}'; } diff --git a/src/main/java/nextstep/courses/domain/EnrollmentRule.java b/src/main/java/nextstep/courses/domain/EnrollmentRule.java index d2399ad61c..1f1c9b7693 100644 --- a/src/main/java/nextstep/courses/domain/EnrollmentRule.java +++ b/src/main/java/nextstep/courses/domain/EnrollmentRule.java @@ -8,4 +8,8 @@ public interface EnrollmentRule { void validateCapacity(int enrolledCount); SessionType getType(); + + int getPrice(); + + int getCapacity(); } diff --git a/src/main/java/nextstep/courses/domain/FreeEnrollmentRule.java b/src/main/java/nextstep/courses/domain/FreeEnrollmentRule.java index a97102702f..2a5c11d9a6 100644 --- a/src/main/java/nextstep/courses/domain/FreeEnrollmentRule.java +++ b/src/main/java/nextstep/courses/domain/FreeEnrollmentRule.java @@ -25,4 +25,14 @@ public void validateCapacity(int enrolledCount) { public SessionType getType() { return SessionType.FREE; } + + @Override + public int getPrice() { + return 0; + } + + @Override + public int getCapacity() { + return 0; + } } diff --git a/src/main/java/nextstep/courses/domain/ImageFile.java b/src/main/java/nextstep/courses/domain/ImageFile.java index 065344256f..491e0b8be6 100644 --- a/src/main/java/nextstep/courses/domain/ImageFile.java +++ b/src/main/java/nextstep/courses/domain/ImageFile.java @@ -1,5 +1,7 @@ package nextstep.courses.domain; +import java.util.Objects; + public class ImageFile { private static final long MAX_SIZE = 1024 * 1024; // 1MB private static final int MIN_WIDTH_PX = 300; @@ -7,29 +9,54 @@ public class ImageFile { private static final int WIDTH_RATIO = 3; private static final int HEIGHT_RATIO = 2; - private long size; - private ImageType imageType; - private int width; - private int height; + private Long id; + private final long size; + private final ImageType imageType; + private final int width; + private final int height; + public ImageFile(Long id) { + this(1L, MAX_SIZE, "png", MIN_WIDTH_PX, MIN_HEIGHT_PX); + } public ImageFile(long size) { - this(size, "png", 300, 200); + this(1L, size, "png", MIN_WIDTH_PX , MIN_HEIGHT_PX); } public ImageFile(long size, String imageType, int width, int height) { + this(1L, size, imageType, width, height); + } + + public ImageFile(Long id, long size, String imageType, int width, int height) { validateSize(size); validateDimensions(width, height); ImageType type = ImageType.getType(imageType); validateImageType(type); + this.id = id; this.size = size; this.imageType = type; this.width = width; this.height = height; } + public long getSize() { + return this.size; + } + + public ImageType getImageType() { + return this.imageType; + } + + public int getWidth() { + return this.width; + } + + public int getHeight() { + return this.height; + } + private void validateImageType(ImageType type) { if (type == ImageType.UNKNOWN) { throw new IllegalArgumentException("지원하지 않는 이미지 타입입니다. (허용 형식: gif, jpg/jpeg, png, svg)"); @@ -61,4 +88,31 @@ private void validateSize(long size) { throw new IllegalArgumentException("이미지 파일 크기는 1MB 이하여야 합니다. (현재: " + size + ")"); } } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + ImageFile imageFile = (ImageFile) o; + return size == imageFile.size && width == imageFile.width && height == imageFile.height && Objects.equals(id, imageFile.id) && imageType == imageFile.imageType; + } + + @Override + public int hashCode() { + return Objects.hash(id, size, imageType, width, height); + } + + @Override + public String toString() { + return "ImageFile{" + + "id=" + id + + ", size=" + size + + ", imageType=" + imageType + + ", width=" + width + + ", height=" + height + + '}'; + } + + public Long getImageId() { + return this.id; + } } diff --git a/src/main/java/nextstep/courses/domain/PaidEnrollmentRule.java b/src/main/java/nextstep/courses/domain/PaidEnrollmentRule.java index 8294a63f8b..951738f648 100644 --- a/src/main/java/nextstep/courses/domain/PaidEnrollmentRule.java +++ b/src/main/java/nextstep/courses/domain/PaidEnrollmentRule.java @@ -4,11 +4,11 @@ public class PaidEnrollmentRule implements EnrollmentRule { private final Money price; private final Capacity capacity; - PaidEnrollmentRule(int price, int capacity) { + public PaidEnrollmentRule(int price, int capacity) { this(new Money(price), new Capacity(capacity)); } - PaidEnrollmentRule(Money price, Capacity capacity) { + public PaidEnrollmentRule(Money price, Capacity capacity) { this.price = price; this.capacity = capacity; } @@ -37,4 +37,14 @@ public void validateCapacity(int enrolledCount) { public SessionType getType() { return SessionType.PAID; } + + @Override + public int getPrice() { + return this.price.getMoney(); + } + + @Override + public int getCapacity() { + return this.capacity.getCapacity(); + } } diff --git a/src/main/java/nextstep/courses/domain/Session.java b/src/main/java/nextstep/courses/domain/Session.java index 1be402d29d..adc61d7f8f 100644 --- a/src/main/java/nextstep/courses/domain/Session.java +++ b/src/main/java/nextstep/courses/domain/Session.java @@ -1,5 +1,8 @@ package nextstep.courses.domain; +import java.time.LocalDateTime; +import java.util.Objects; + public class Session { private final Long id; private final ImageFile imageFile; @@ -8,8 +11,8 @@ public class Session { private final EnrollmentRule enrollmentRule; private final Enrollments enrollments; - public Session(Long id, ImageFile imageFile, SessionPeriod period, SessionStatus sessionStatus, EnrollmentRule enrollmentRule) { - this(id, imageFile, period, sessionStatus, enrollmentRule, new Enrollments()); + public Session(ImageFile imageFile, SessionPeriod period, SessionStatus sessionStatus, EnrollmentRule enrollmentRule, Enrollments enrollments) { + this(null, imageFile, period, sessionStatus, enrollmentRule, enrollments); } public Session(Long id, ImageFile imageFile, SessionPeriod period, SessionStatus sessionStatus, EnrollmentRule enrollmentRule, Enrollments enrollments) { @@ -39,9 +42,61 @@ public Long getId() { return id; } + public Long getImageId() { + return this.imageFile.getImageId(); + } + + public String getSessionStatus() { + return this.sessionStatus.toString(); + } + + public int getPrice() { + return this.enrollmentRule.getPrice(); + } + + public int getCapacity() { + return this.enrollmentRule.getCapacity(); + } + + public LocalDateTime getStartTime() { + return this.period.getStartTime(); + } + + public LocalDateTime getEndTime() { + return this.period.getEndTime(); + } + + public SessionPeriod getPeriod() { + return this.period; + } + private void validationRecruiting() { if (!sessionStatus.enableRecruiting()) { throw new IllegalArgumentException("모집중인 강의만 수강 신청할 수 있습니다."); } } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + Session session = (Session) o; + return Objects.equals(id, session.id) && Objects.equals(imageFile, session.imageFile) && Objects.equals(period, session.period) && sessionStatus == session.sessionStatus && Objects.equals(enrollmentRule, session.enrollmentRule) && Objects.equals(enrollments, session.enrollments); + } + + @Override + public int hashCode() { + return Objects.hash(id, imageFile, period, sessionStatus, enrollmentRule, enrollments); + } + + @Override + public String toString() { + return "Session{" + + "id=" + id + + ", imageFile=" + imageFile + + ", period=" + period + + ", sessionStatus=" + sessionStatus + + ", enrollmentRule=" + enrollmentRule + + ", enrollments=" + enrollments + + '}'; + } } diff --git a/src/main/java/nextstep/courses/domain/SessionPeriod.java b/src/main/java/nextstep/courses/domain/SessionPeriod.java index c62dfd6b22..8530503489 100644 --- a/src/main/java/nextstep/courses/domain/SessionPeriod.java +++ b/src/main/java/nextstep/courses/domain/SessionPeriod.java @@ -1,6 +1,7 @@ package nextstep.courses.domain; import java.time.LocalDateTime; +import java.util.Objects; public class SessionPeriod { private LocalDateTime startTime; @@ -12,6 +13,14 @@ public SessionPeriod(LocalDateTime startTime, LocalDateTime endTime) { this.endTime = endTime; } + public LocalDateTime getStartTime() { + return this.startTime; + } + + public LocalDateTime getEndTime() { + return this.endTime; + } + private void validationPeriod(LocalDateTime startTime, LocalDateTime endTime) { if (startTime == null || endTime == null) { throw new IllegalArgumentException("시작 시간과 종료 시간은 필수입니다."); @@ -21,4 +30,24 @@ private void validationPeriod(LocalDateTime startTime, LocalDateTime endTime) { throw new IllegalArgumentException("종료 시간은 시작 시간 이후여야 합니다."); } } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + SessionPeriod that = (SessionPeriod) o; + return Objects.equals(startTime, that.startTime) && Objects.equals(endTime, that.endTime); + } + + @Override + public int hashCode() { + return Objects.hash(startTime, endTime); + } + + @Override + public String toString() { + return "SessionPeriod{" + + "startTime=" + startTime + + ", endTime=" + endTime + + '}'; + } } diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java index f9122cbe33..79409f765e 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java @@ -1,7 +1,7 @@ package nextstep.courses.infrastructure; import nextstep.courses.domain.Course; -import nextstep.courses.domain.CourseRepository; +import nextstep.courses.repository.CourseRepository; import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java new file mode 100644 index 0000000000..afce337cfb --- /dev/null +++ b/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java @@ -0,0 +1,35 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.Enrollment; +import nextstep.courses.repository.EnrollmentRepository; +import org.springframework.jdbc.core.JdbcOperations; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +@Repository("enrollmentRepository") +public class JdbcEnrollmentRepository implements EnrollmentRepository { + + private JdbcOperations jdbcTemplate; + + public JdbcEnrollmentRepository(JdbcOperations jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Override + public int save(Enrollment enrollment) { + String sql = "insert into enrollment (student_id, session_id) values (?, ?)"; + return jdbcTemplate.update(sql, enrollment.getStudentId(), enrollment.getSessionId()); + } + + @Override + public Enrollment findById(Long id) { + String sql = "select * from enrollment where id = ?"; + RowMapper rowMapper = (rs, rowNum) -> new Enrollment( + rs.getLong("id"), + rs.getLong("student_id"), + rs.getLong("session_id") + ); + + return jdbcTemplate.queryForObject(sql, rowMapper, id); + } +} diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcImageFileRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcImageFileRepository.java new file mode 100644 index 0000000000..982600af80 --- /dev/null +++ b/src/main/java/nextstep/courses/infrastructure/JdbcImageFileRepository.java @@ -0,0 +1,44 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.ImageFile; +import nextstep.courses.repository.ImageFileRepository; +import org.springframework.jdbc.core.JdbcOperations; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +@Repository("imageFileRepository") +public class JdbcImageFileRepository implements ImageFileRepository { + + private JdbcOperations jdbcTemplate; + + public JdbcImageFileRepository(JdbcOperations jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Override + public int save(ImageFile imageFile) { + String sql = "insert into image_file (size, image_type, width, height) values (?, ?, ?, ?)"; + + return jdbcTemplate.update(sql, + imageFile.getSize(), + imageFile.getImageType().toString(), + imageFile.getWidth(), + imageFile.getHeight() + ); + + } + + @Override + public ImageFile findById(long id) { + String sql = "select * from image_file where id = ?"; + RowMapper rowMapper = (rs, rowNum) -> new ImageFile( + rs.getLong("id"), + rs.getLong("size"), + rs.getString("image_type"), + rs.getInt("width"), + rs.getInt("height") + ); + + return jdbcTemplate.queryForObject(sql, rowMapper, id); + } +} diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java new file mode 100644 index 0000000000..3f24a9459b --- /dev/null +++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java @@ -0,0 +1,80 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.*; +import nextstep.courses.repository.SessionRepository; +import org.springframework.jdbc.core.JdbcOperations; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; + + +@Repository("sessionRepository") +public class JdbcSessionRepository implements SessionRepository { + + private JdbcOperations jdbcTemplate; + + public JdbcSessionRepository(JdbcOperations jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Override + public int save(Session session) { + String sql = "insert into session (image_id, session_status, price, capacity, start_time, end_time) values (?, ?, ?, ?, ?, ?)"; + + + return jdbcTemplate.update(sql, + session.getImageId(), + session.getSessionStatus(), + session.getPrice(), + session.getCapacity(), + session.getStartTime(), + session.getEndTime()); + } + + @Override + public Session findById(long id) { + String sql = "select * from session where id = ?"; + + return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> { + + // 1️⃣ ImageFile (지금은 ID만 복원) + ImageFile imageFile = new ImageFile(rs.getLong("image_id")); + + // 2️⃣ SessionPeriod + SessionPeriod period = new SessionPeriod( + rs.getObject("start_time", LocalDateTime.class), + rs.getObject("end_time", LocalDateTime.class) + ); + + // 3️⃣ SessionStatus + SessionStatus status = + SessionStatus.valueOf(rs.getString("session_status")); + + // 4️⃣ EnrollmentRule + Integer price = rs.getObject("price", Integer.class); + + EnrollmentRule enrollmentRule; + if (price != null) { + enrollmentRule = new PaidEnrollmentRule( + price, + rs.getInt("capacity") + ); + } else { + enrollmentRule = new FreeEnrollmentRule(); + } + + // 5️⃣ Enrollments (조회 시점에서는 비어 있음) + Enrollments enrollments = new Enrollments(); + + return new Session( + rs.getLong("id"), + imageFile, + period, + status, + enrollmentRule, + enrollments + ); + }, id); + } +} diff --git a/src/main/java/nextstep/courses/domain/CourseRepository.java b/src/main/java/nextstep/courses/repository/CourseRepository.java similarity index 56% rename from src/main/java/nextstep/courses/domain/CourseRepository.java rename to src/main/java/nextstep/courses/repository/CourseRepository.java index 6aaeb638d1..0680a57a23 100644 --- a/src/main/java/nextstep/courses/domain/CourseRepository.java +++ b/src/main/java/nextstep/courses/repository/CourseRepository.java @@ -1,4 +1,6 @@ -package nextstep.courses.domain; +package nextstep.courses.repository; + +import nextstep.courses.domain.Course; public interface CourseRepository { int save(Course course); diff --git a/src/main/java/nextstep/courses/repository/EnrollmentRepository.java b/src/main/java/nextstep/courses/repository/EnrollmentRepository.java new file mode 100644 index 0000000000..36cbe727d7 --- /dev/null +++ b/src/main/java/nextstep/courses/repository/EnrollmentRepository.java @@ -0,0 +1,9 @@ +package nextstep.courses.repository; + +import nextstep.courses.domain.Enrollment; + +public interface EnrollmentRepository { + int save(Enrollment enrollment); + + Enrollment findById(Long id); +} diff --git a/src/main/java/nextstep/courses/repository/ImageFileRepository.java b/src/main/java/nextstep/courses/repository/ImageFileRepository.java new file mode 100644 index 0000000000..cdc9e38402 --- /dev/null +++ b/src/main/java/nextstep/courses/repository/ImageFileRepository.java @@ -0,0 +1,9 @@ +package nextstep.courses.repository; + +import nextstep.courses.domain.ImageFile; + +public interface ImageFileRepository { + int save(ImageFile imageFile); + + ImageFile findById(long id); +} diff --git a/src/main/java/nextstep/courses/repository/SessionRepository.java b/src/main/java/nextstep/courses/repository/SessionRepository.java new file mode 100644 index 0000000000..28a70a7fb6 --- /dev/null +++ b/src/main/java/nextstep/courses/repository/SessionRepository.java @@ -0,0 +1,9 @@ +package nextstep.courses.repository; + + +import nextstep.courses.domain.Session; + +public interface SessionRepository { + int save(Session session); + Session findById(long id); +} diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 8d5a988c8b..ef0f6df980 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -48,3 +48,30 @@ create table delete_history ( deleted_by_id bigint, primary key (id) ); + +create table enrollment ( + id bigint generated by default as identity, + student_id bigint, + session_id bigint, + primary key (id) +); + +create table image_file ( + id bigint generated by default as identity, + size bigint, + image_type varchar(255), + width int, + height int, + primary key (id) +); + +create table session ( + id bigint generated by default as identity, + image_id bigint not null, + session_status varchar(20) not null, + price int, + capacity int, + start_time timestamp not null, + end_time timestamp not null, + primary key (id) +) diff --git a/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java index f087fc0ad2..885346974e 100644 --- a/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java +++ b/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java @@ -1,7 +1,7 @@ package nextstep.courses.infrastructure; import nextstep.courses.domain.Course; -import nextstep.courses.domain.CourseRepository; +import nextstep.courses.repository.CourseRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; diff --git a/src/test/java/nextstep/courses/repository/EnrollmentRepositoryTest.java b/src/test/java/nextstep/courses/repository/EnrollmentRepositoryTest.java new file mode 100644 index 0000000000..b584a31718 --- /dev/null +++ b/src/test/java/nextstep/courses/repository/EnrollmentRepositoryTest.java @@ -0,0 +1,34 @@ +package nextstep.courses.repository; + +import nextstep.courses.domain.Enrollment; +import nextstep.courses.infrastructure.JdbcEnrollmentRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.context.annotation.Import; + +import static org.assertj.core.api.Assertions.assertThat; + +@JdbcTest +@Import(JdbcEnrollmentRepository.class) +public class EnrollmentRepositoryTest { + + @Autowired + EnrollmentRepository enrollmentRepository; + + @Test + void save() { + int save = enrollmentRepository.save(new Enrollment(1L, 1L)); + + assertThat(save).isEqualTo(1); + } + + @Test + void find() { + enrollmentRepository.save(new Enrollment(1L, 1L)); + + Enrollment byId = enrollmentRepository.findById(1L); + + assertThat(byId).isEqualTo(new Enrollment(1L, 1L)); + } +} diff --git a/src/test/java/nextstep/courses/repository/ImageFileRepositoryTest.java b/src/test/java/nextstep/courses/repository/ImageFileRepositoryTest.java new file mode 100644 index 0000000000..dbd0da08e4 --- /dev/null +++ b/src/test/java/nextstep/courses/repository/ImageFileRepositoryTest.java @@ -0,0 +1,34 @@ +package nextstep.courses.repository; + +import nextstep.courses.domain.Enrollment; +import nextstep.courses.domain.ImageFile; +import nextstep.courses.domain.ImageType; +import nextstep.courses.infrastructure.JdbcImageFileRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.context.annotation.Import; + +import static org.assertj.core.api.Assertions.assertThat; + +@JdbcTest +@Import(JdbcImageFileRepository.class) +public class ImageFileRepositoryTest { + + @Autowired + ImageFileRepository imageFileRepository; + + @Test + void save() { + int save = imageFileRepository.save(new ImageFile(1024 * 1024, "jpg", 300, 200)); + + assertThat(save).isEqualTo(1); + } + + @Test + void find() { + imageFileRepository.save(new ImageFile(1024 * 1024, "jpg", 300, 200)); + + assertThat((imageFileRepository.findById(1L))).isEqualTo(new ImageFile(1L,1024 * 1024, "jpg", 300, 200)); + } +} diff --git a/src/test/java/nextstep/courses/repository/SessionRepositoryTest.java b/src/test/java/nextstep/courses/repository/SessionRepositoryTest.java new file mode 100644 index 0000000000..6c529caf47 --- /dev/null +++ b/src/test/java/nextstep/courses/repository/SessionRepositoryTest.java @@ -0,0 +1,55 @@ +package nextstep.courses.repository; + +import nextstep.courses.domain.*; +import nextstep.courses.infrastructure.JdbcSessionRepository; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.context.annotation.Import; + +import java.time.LocalDateTime; + +import static org.assertj.core.api.Assertions.*; + +@JdbcTest +@Import(JdbcSessionRepository.class) +public class SessionRepositoryTest { + + @Autowired + JdbcSessionRepository jdbcSessionRepository; + + @Test + void save() { + + ImageFile imageFile = new ImageFile(1024*1024); + SessionPeriod period = new SessionPeriod(LocalDateTime.now(), LocalDateTime.now().plusDays(7)); + SessionStatus sessionStatus = SessionStatus.RECRUITING; + EnrollmentRule enrollmentRule = new PaidEnrollmentRule(50000, 10); + Enrollments enrollments = new Enrollments(); + + Session session = new Session(imageFile, period, sessionStatus, enrollmentRule, enrollments); + + int save = jdbcSessionRepository.save(session); + + assertThat(save).isEqualTo(1); + } + + @Test + void find() { + ImageFile imageFile = new ImageFile(1); + SessionPeriod period = new SessionPeriod(LocalDateTime.now(), LocalDateTime.now().plusDays(7)); + SessionStatus sessionStatus = SessionStatus.RECRUITING; + EnrollmentRule enrollmentRule = new PaidEnrollmentRule(50000, 10); + Enrollments enrollments = new Enrollments(); + + Session session = new Session(imageFile, period, sessionStatus, enrollmentRule, enrollments); + + jdbcSessionRepository.save(session); + + Session found = jdbcSessionRepository.findById(1L); + + assertThat(found.getSessionStatus()).isEqualTo(session.getSessionStatus()); + assertThat(found.getPeriod()).isEqualTo(session.getPeriod()); + } +} From 51800b1e915e621f6912307961713f98c1ea53d9 Mon Sep 17 00:00:00 2001 From: ho-jun97 Date: Fri, 26 Dec 2025 22:21:26 +0900 Subject: [PATCH 3/6] =?UTF-8?q?refactor:=20EnrollmentRule=EC=97=90=20?= =?UTF-8?q?=EC=A0=9C=EA=B3=B5=ED=95=98=EB=8A=94=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=B5=9C=EC=86=8C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/courses/domain/EnrollmentRule.java | 4 ---- .../courses/domain/FreeEnrollmentRule.java | 10 ---------- .../courses/domain/PaidEnrollmentRule.java | 2 -- .../java/nextstep/courses/domain/Session.java | 16 ++++++++++++---- 4 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/main/java/nextstep/courses/domain/EnrollmentRule.java b/src/main/java/nextstep/courses/domain/EnrollmentRule.java index 1f1c9b7693..d2399ad61c 100644 --- a/src/main/java/nextstep/courses/domain/EnrollmentRule.java +++ b/src/main/java/nextstep/courses/domain/EnrollmentRule.java @@ -8,8 +8,4 @@ public interface EnrollmentRule { void validateCapacity(int enrolledCount); SessionType getType(); - - int getPrice(); - - int getCapacity(); } diff --git a/src/main/java/nextstep/courses/domain/FreeEnrollmentRule.java b/src/main/java/nextstep/courses/domain/FreeEnrollmentRule.java index 2a5c11d9a6..a97102702f 100644 --- a/src/main/java/nextstep/courses/domain/FreeEnrollmentRule.java +++ b/src/main/java/nextstep/courses/domain/FreeEnrollmentRule.java @@ -25,14 +25,4 @@ public void validateCapacity(int enrolledCount) { public SessionType getType() { return SessionType.FREE; } - - @Override - public int getPrice() { - return 0; - } - - @Override - public int getCapacity() { - return 0; - } } diff --git a/src/main/java/nextstep/courses/domain/PaidEnrollmentRule.java b/src/main/java/nextstep/courses/domain/PaidEnrollmentRule.java index 951738f648..8d77456fa7 100644 --- a/src/main/java/nextstep/courses/domain/PaidEnrollmentRule.java +++ b/src/main/java/nextstep/courses/domain/PaidEnrollmentRule.java @@ -38,12 +38,10 @@ public SessionType getType() { return SessionType.PAID; } - @Override public int getPrice() { return this.price.getMoney(); } - @Override public int getCapacity() { return this.capacity.getCapacity(); } diff --git a/src/main/java/nextstep/courses/domain/Session.java b/src/main/java/nextstep/courses/domain/Session.java index adc61d7f8f..e58690f975 100644 --- a/src/main/java/nextstep/courses/domain/Session.java +++ b/src/main/java/nextstep/courses/domain/Session.java @@ -50,12 +50,20 @@ public String getSessionStatus() { return this.sessionStatus.toString(); } - public int getPrice() { - return this.enrollmentRule.getPrice(); + public Integer getPrice() { + if (enrollmentRule.getType().equals(SessionType.PAID)) { + return ((PaidEnrollmentRule) this.enrollmentRule).getPrice(); + } + + return null; } - public int getCapacity() { - return this.enrollmentRule.getCapacity(); + public Integer getCapacity() { + if (enrollmentRule.getType().equals(SessionType.PAID)) { + return ((PaidEnrollmentRule) this.enrollmentRule).getCapacity(); + } + + return null; } public LocalDateTime getStartTime() { From 1a05df3c77903230c3c902ac885f107871a9674d Mon Sep 17 00:00:00 2001 From: ho-jun97 Date: Sat, 27 Dec 2025 14:13:25 +0900 Subject: [PATCH 4/6] =?UTF-8?q?refactor:=20image=5Ffile=20join=20=EB=B0=8F?= =?UTF-8?q?=20=EA=B0=81=20=EC=A0=80=EC=9E=A5=20=ED=9B=84=20Long=20=20id=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20keyHolder=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 --- .../nextstep/courses/domain/ImageFile.java | 3 - .../JdbcEnrollmentRepository.java | 19 ++++- .../JdbcImageFileRepository.java | 23 ++++-- .../infrastructure/JdbcSessionRepository.java | 74 ++++++++++++++++--- .../repository/EnrollmentRepository.java | 2 +- .../repository/ImageFileRepository.java | 2 +- .../courses/repository/SessionRepository.java | 2 +- .../repository/EnrollmentRepositoryTest.java | 10 +-- .../repository/ImageFileRepositoryTest.java | 8 +- .../repository/SessionRepositoryTest.java | 19 +++-- 10 files changed, 120 insertions(+), 42 deletions(-) diff --git a/src/main/java/nextstep/courses/domain/ImageFile.java b/src/main/java/nextstep/courses/domain/ImageFile.java index 491e0b8be6..26e4c57d1e 100644 --- a/src/main/java/nextstep/courses/domain/ImageFile.java +++ b/src/main/java/nextstep/courses/domain/ImageFile.java @@ -15,9 +15,6 @@ public class ImageFile { private final int width; private final int height; - public ImageFile(Long id) { - this(1L, MAX_SIZE, "png", MIN_WIDTH_PX, MIN_HEIGHT_PX); - } public ImageFile(long size) { this(1L, size, "png", MIN_WIDTH_PX , MIN_HEIGHT_PX); diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java index afce337cfb..d5352f1494 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java @@ -3,9 +3,14 @@ import nextstep.courses.domain.Enrollment; import nextstep.courses.repository.EnrollmentRepository; import org.springframework.jdbc.core.JdbcOperations; +import org.springframework.jdbc.core.PreparedStatementCreator; import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Repository; +import java.sql.PreparedStatement; + @Repository("enrollmentRepository") public class JdbcEnrollmentRepository implements EnrollmentRepository { @@ -16,9 +21,19 @@ public JdbcEnrollmentRepository(JdbcOperations jdbcTemplate) { } @Override - public int save(Enrollment enrollment) { + public Long save(Enrollment enrollment) { String sql = "insert into enrollment (student_id, session_id) values (?, ?)"; - return jdbcTemplate.update(sql, enrollment.getStudentId(), enrollment.getSessionId()); + + KeyHolder keyHolder = new GeneratedKeyHolder(); + + jdbcTemplate.update(connect -> { + PreparedStatement ps = connect.prepareStatement(sql, new String[]{"id"}); + ps.setLong(1, enrollment.getStudentId()); + ps.setLong(2, enrollment.getSessionId()); + return ps; + }, keyHolder); + + return keyHolder.getKey().longValue(); } @Override diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcImageFileRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcImageFileRepository.java index 982600af80..9debcdde10 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcImageFileRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcImageFileRepository.java @@ -4,8 +4,12 @@ import nextstep.courses.repository.ImageFileRepository; import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Repository; +import java.sql.PreparedStatement; + @Repository("imageFileRepository") public class JdbcImageFileRepository implements ImageFileRepository { @@ -16,16 +20,21 @@ public JdbcImageFileRepository(JdbcOperations jdbcTemplate) { } @Override - public int save(ImageFile imageFile) { + public Long save(ImageFile imageFile) { String sql = "insert into image_file (size, image_type, width, height) values (?, ?, ?, ?)"; - return jdbcTemplate.update(sql, - imageFile.getSize(), - imageFile.getImageType().toString(), - imageFile.getWidth(), - imageFile.getHeight() - ); + KeyHolder keyHolder = new GeneratedKeyHolder(); + + jdbcTemplate.update(connect -> { + PreparedStatement ps = connect.prepareStatement(sql, new String[]{"id"}); + ps.setLong(1, imageFile.getSize()); + ps.setString(2, imageFile.getImageType().toString()); + ps.setInt(3, imageFile.getWidth()); + ps.setInt(4, imageFile.getHeight()); + return ps; + }, keyHolder); + return keyHolder.getKey().longValue(); } @Override diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java index 3f24a9459b..7eb5fba23a 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java @@ -3,9 +3,14 @@ import nextstep.courses.domain.*; import nextstep.courses.repository.SessionRepository; import org.springframework.jdbc.core.JdbcOperations; -import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Repository; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.sql.Types; import java.time.LocalDateTime; @@ -19,27 +24,72 @@ public JdbcSessionRepository(JdbcOperations jdbcTemplate) { } @Override - public int save(Session session) { + public Long save(Session session) { String sql = "insert into session (image_id, session_status, price, capacity, start_time, end_time) values (?, ?, ?, ?, ?, ?)"; + KeyHolder keyHolder = new GeneratedKeyHolder(); + + jdbcTemplate.update(connect -> { + PreparedStatement ps = connect.prepareStatement(sql, new String[]{"id"}); + ps.setLong(1, session.getImageId()); + ps.setString(2 , session.getSessionStatus()); + ps.setInt(3 , session.getPrice()); + ps.setInt(4 , session.getCapacity()); + extractedPrice(session, ps); + extractedCapacity(session, ps); + ps.setTimestamp(5, Timestamp.valueOf(session.getStartTime())); + ps.setTimestamp(6, Timestamp.valueOf(session.getEndTime())); + return ps; + }, keyHolder); + + return keyHolder.getKey().longValue(); + } + + private static void extractedCapacity(Session session, PreparedStatement ps) throws SQLException { + if (session.getCapacity() != null) { + ps.setInt(4, session.getCapacity()); + } else { + ps.setNull(4, Types.INTEGER); + } + } - return jdbcTemplate.update(sql, - session.getImageId(), - session.getSessionStatus(), - session.getPrice(), - session.getCapacity(), - session.getStartTime(), - session.getEndTime()); + private static void extractedPrice(Session session, PreparedStatement ps) throws SQLException { + if (session.getPrice() != null) { + ps.setInt(3, session.getPrice()); + } else { + ps.setNull(3, Types.INTEGER); + } } @Override public Session findById(long id) { - String sql = "select * from session where id = ?"; + String sql = "select " + + "s.id, " + + "s.session_status, " + + "s.price, " + + "s.capacity, " + + "s.start_time, " + + "s.end_time, " + + "i.id AS image_id, " + + "i.size, " + + "i.image_type, " + + "i.width, " + + "i.height " + + "from session s " + + "join image_file i " + + "on s.image_id = i.id " + + "where s.id = ?"; return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> { // 1️⃣ ImageFile (지금은 ID만 복원) - ImageFile imageFile = new ImageFile(rs.getLong("image_id")); + ImageFile imageFile = new ImageFile( + rs.getLong("image_id"), + rs.getLong("size"), + rs.getString("image_type"), + rs.getInt("width"), + rs.getInt("height") + ); // 2️⃣ SessionPeriod SessionPeriod period = new SessionPeriod( @@ -77,4 +127,6 @@ public Session findById(long id) { ); }, id); } + + } diff --git a/src/main/java/nextstep/courses/repository/EnrollmentRepository.java b/src/main/java/nextstep/courses/repository/EnrollmentRepository.java index 36cbe727d7..5527962a9e 100644 --- a/src/main/java/nextstep/courses/repository/EnrollmentRepository.java +++ b/src/main/java/nextstep/courses/repository/EnrollmentRepository.java @@ -3,7 +3,7 @@ import nextstep.courses.domain.Enrollment; public interface EnrollmentRepository { - int save(Enrollment enrollment); + Long save(Enrollment enrollment); Enrollment findById(Long id); } diff --git a/src/main/java/nextstep/courses/repository/ImageFileRepository.java b/src/main/java/nextstep/courses/repository/ImageFileRepository.java index cdc9e38402..2606fff38d 100644 --- a/src/main/java/nextstep/courses/repository/ImageFileRepository.java +++ b/src/main/java/nextstep/courses/repository/ImageFileRepository.java @@ -3,7 +3,7 @@ import nextstep.courses.domain.ImageFile; public interface ImageFileRepository { - int save(ImageFile imageFile); + Long save(ImageFile imageFile); ImageFile findById(long id); } diff --git a/src/main/java/nextstep/courses/repository/SessionRepository.java b/src/main/java/nextstep/courses/repository/SessionRepository.java index 28a70a7fb6..4ae0de287a 100644 --- a/src/main/java/nextstep/courses/repository/SessionRepository.java +++ b/src/main/java/nextstep/courses/repository/SessionRepository.java @@ -4,6 +4,6 @@ import nextstep.courses.domain.Session; public interface SessionRepository { - int save(Session session); + Long save(Session session); Session findById(long id); } diff --git a/src/test/java/nextstep/courses/repository/EnrollmentRepositoryTest.java b/src/test/java/nextstep/courses/repository/EnrollmentRepositoryTest.java index b584a31718..5448cb6af4 100644 --- a/src/test/java/nextstep/courses/repository/EnrollmentRepositoryTest.java +++ b/src/test/java/nextstep/courses/repository/EnrollmentRepositoryTest.java @@ -18,17 +18,17 @@ public class EnrollmentRepositoryTest { @Test void save() { - int save = enrollmentRepository.save(new Enrollment(1L, 1L)); + Long enrollmentId = enrollmentRepository.save(new Enrollment(1L, 1L)); - assertThat(save).isEqualTo(1); + assertThat(enrollmentId).isNotNull(); } @Test void find() { - enrollmentRepository.save(new Enrollment(1L, 1L)); + Long enrollmentId = enrollmentRepository.save(new Enrollment(1L, 1L)); - Enrollment byId = enrollmentRepository.findById(1L); + Enrollment enrollment = enrollmentRepository.findById(enrollmentId); - assertThat(byId).isEqualTo(new Enrollment(1L, 1L)); + assertThat(enrollment).isEqualTo(new Enrollment(1L, 1L)); } } diff --git a/src/test/java/nextstep/courses/repository/ImageFileRepositoryTest.java b/src/test/java/nextstep/courses/repository/ImageFileRepositoryTest.java index dbd0da08e4..6f23f36b39 100644 --- a/src/test/java/nextstep/courses/repository/ImageFileRepositoryTest.java +++ b/src/test/java/nextstep/courses/repository/ImageFileRepositoryTest.java @@ -20,15 +20,15 @@ public class ImageFileRepositoryTest { @Test void save() { - int save = imageFileRepository.save(new ImageFile(1024 * 1024, "jpg", 300, 200)); + Long imageId = imageFileRepository.save(new ImageFile(1024 * 1024, "jpg", 300, 200)); - assertThat(save).isEqualTo(1); + assertThat(imageId).isNotNull(); } @Test void find() { - imageFileRepository.save(new ImageFile(1024 * 1024, "jpg", 300, 200)); + Long imageId = imageFileRepository.save(new ImageFile(1024 * 1024, "jpg", 300, 200)); - assertThat((imageFileRepository.findById(1L))).isEqualTo(new ImageFile(1L,1024 * 1024, "jpg", 300, 200)); + assertThat((imageFileRepository.findById(imageId))).isEqualTo(new ImageFile(1L,1024 * 1024, "jpg", 300, 200)); } } diff --git a/src/test/java/nextstep/courses/repository/SessionRepositoryTest.java b/src/test/java/nextstep/courses/repository/SessionRepositoryTest.java index 6c529caf47..0f4add46aa 100644 --- a/src/test/java/nextstep/courses/repository/SessionRepositoryTest.java +++ b/src/test/java/nextstep/courses/repository/SessionRepositoryTest.java @@ -1,6 +1,7 @@ package nextstep.courses.repository; import nextstep.courses.domain.*; +import nextstep.courses.infrastructure.JdbcImageFileRepository; import nextstep.courses.infrastructure.JdbcSessionRepository; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; @@ -13,15 +14,17 @@ import static org.assertj.core.api.Assertions.*; @JdbcTest -@Import(JdbcSessionRepository.class) +@Import({JdbcSessionRepository.class, JdbcImageFileRepository.class}) public class SessionRepositoryTest { @Autowired JdbcSessionRepository jdbcSessionRepository; + @Autowired + JdbcImageFileRepository jdbcImageFileRepository; + @Test void save() { - ImageFile imageFile = new ImageFile(1024*1024); SessionPeriod period = new SessionPeriod(LocalDateTime.now(), LocalDateTime.now().plusDays(7)); SessionStatus sessionStatus = SessionStatus.RECRUITING; @@ -30,14 +33,16 @@ void save() { Session session = new Session(imageFile, period, sessionStatus, enrollmentRule, enrollments); - int save = jdbcSessionRepository.save(session); + Long sessionId = jdbcSessionRepository.save(session); - assertThat(save).isEqualTo(1); + assertThat(sessionId).isNotNull(); } @Test void find() { - ImageFile imageFile = new ImageFile(1); + ImageFile imageFile = new ImageFile(1024 * 1024, "png", 300 , 200); + jdbcImageFileRepository.save(imageFile); + SessionPeriod period = new SessionPeriod(LocalDateTime.now(), LocalDateTime.now().plusDays(7)); SessionStatus sessionStatus = SessionStatus.RECRUITING; EnrollmentRule enrollmentRule = new PaidEnrollmentRule(50000, 10); @@ -45,9 +50,9 @@ void find() { Session session = new Session(imageFile, period, sessionStatus, enrollmentRule, enrollments); - jdbcSessionRepository.save(session); + Long sessionId = jdbcSessionRepository.save(session); - Session found = jdbcSessionRepository.findById(1L); + Session found = jdbcSessionRepository.findById(sessionId); assertThat(found.getSessionStatus()).isEqualTo(session.getSessionStatus()); assertThat(found.getPeriod()).isEqualTo(session.getPeriod()); From e617a46d197154d28e9cf6d190e298a8c24884ac Mon Sep 17 00:00:00 2001 From: ho-jun97 Date: Sat, 27 Dec 2025 16:33:22 +0900 Subject: [PATCH 5/6] =?UTF-8?q?refactor:=20ImageFile=20=EC=A0=9C=EC=99=B8?= =?UTF-8?q?=ED=95=9C=20=EA=B0=9D=EC=B2=B4=EB=93=A4=20=EB=B6=80=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=9E=90=EC=97=90=EC=84=9C=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/courses/domain/Session.java | 12 ++++++ .../infrastructure/JdbcSessionRepository.java | 43 +++++++++++++++++++ .../repository/SessionRepositoryTest.java | 2 +- 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/main/java/nextstep/courses/domain/Session.java b/src/main/java/nextstep/courses/domain/Session.java index e58690f975..ee3581075e 100644 --- a/src/main/java/nextstep/courses/domain/Session.java +++ b/src/main/java/nextstep/courses/domain/Session.java @@ -11,6 +11,10 @@ public class Session { private final EnrollmentRule enrollmentRule; private final Enrollments enrollments; + public Session(Long id,ImageFile imageFile, LocalDateTime startTime, LocalDateTime endTime, String sessionStatus, Integer price, Integer capacity) { + this(id, imageFile, new SessionPeriod(startTime, endTime), SessionStatus.valueOf(sessionStatus), allocateEnrollmentRule(price, capacity), new Enrollments()); + } + public Session(ImageFile imageFile, SessionPeriod period, SessionStatus sessionStatus, EnrollmentRule enrollmentRule, Enrollments enrollments) { this(null, imageFile, period, sessionStatus, enrollmentRule, enrollments); } @@ -78,6 +82,14 @@ public SessionPeriod getPeriod() { return this.period; } + private static EnrollmentRule allocateEnrollmentRule(Integer price, Integer capacity) { + if (price != null) { + return new PaidEnrollmentRule(price, capacity); + } + + return new FreeEnrollmentRule(); + } + private void validationRecruiting() { if (!sessionStatus.enableRecruiting()) { throw new IllegalArgumentException("모집중인 강의만 수강 신청할 수 있습니다."); diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java index 7eb5fba23a..84121ff0bf 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java @@ -129,4 +129,47 @@ public Session findById(long id) { } + public Session findById2(long id) { + String sql = "select " + + "s.id, " + + "s.session_status, " + + "s.price, " + + "s.capacity, " + + "s.start_time, " + + "s.end_time, " + + "i.id AS image_id, " + + "i.size, " + + "i.image_type, " + + "i.width, " + + "i.height " + + "from session s " + + "join image_file i " + + "on s.image_id = i.id " + + "where s.id = ?"; + + return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> { + + ImageFile imageFile = new ImageFile( + rs.getLong("image_id"), + rs.getLong("size"), + rs.getString("image_type"), + rs.getInt("width"), + rs.getInt("height") + ); + + Session session = new Session( + rs.getLong("id"), + imageFile, + rs.getObject("start_time", LocalDateTime.class), + rs.getObject("end_time", LocalDateTime.class), + rs.getString("session_status"), + rs.getObject("price", Integer.class), + rs.getObject("capacity", Integer.class) + ); + + return session; + }, id); + } + + } diff --git a/src/test/java/nextstep/courses/repository/SessionRepositoryTest.java b/src/test/java/nextstep/courses/repository/SessionRepositoryTest.java index 0f4add46aa..c97f7ed4b9 100644 --- a/src/test/java/nextstep/courses/repository/SessionRepositoryTest.java +++ b/src/test/java/nextstep/courses/repository/SessionRepositoryTest.java @@ -52,7 +52,7 @@ void find() { Long sessionId = jdbcSessionRepository.save(session); - Session found = jdbcSessionRepository.findById(sessionId); + Session found = jdbcSessionRepository.findById2(sessionId); assertThat(found.getSessionStatus()).isEqualTo(session.getSessionStatus()); assertThat(found.getPeriod()).isEqualTo(session.getPeriod()); From 55b9cda1e542102ead3963ed9ca233390305823b Mon Sep 17 00:00:00 2001 From: ho-jun97 Date: Sat, 27 Dec 2025 20:53:43 +0900 Subject: [PATCH 6/6] =?UTF-8?q?feat:=20service=20layer=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/courses/domain/Session.java | 4 ++ .../JdbcEnrollmentRepository.java | 16 +++++ .../infrastructure/JdbcSessionRepository.java | 68 ------------------- .../repository/EnrollmentRepository.java | 4 ++ .../courses/service/SessionService.java | 53 +++++++++++++++ .../courses/domain/SessionBuilder.java | 2 +- .../repository/ImageFileRepositoryTest.java | 2 - .../repository/SessionRepositoryTest.java | 6 +- .../courses/service/SessionServiceTest.java | 49 +++++++++++++ 9 files changed, 130 insertions(+), 74 deletions(-) create mode 100644 src/main/java/nextstep/courses/service/SessionService.java create mode 100644 src/test/java/nextstep/courses/service/SessionServiceTest.java diff --git a/src/main/java/nextstep/courses/domain/Session.java b/src/main/java/nextstep/courses/domain/Session.java index ee3581075e..4ba90de398 100644 --- a/src/main/java/nextstep/courses/domain/Session.java +++ b/src/main/java/nextstep/courses/domain/Session.java @@ -15,6 +15,10 @@ public Session(Long id,ImageFile imageFile, LocalDateTime startTime, LocalDateTi this(id, imageFile, new SessionPeriod(startTime, endTime), SessionStatus.valueOf(sessionStatus), allocateEnrollmentRule(price, capacity), new Enrollments()); } + public Session(ImageFile imageFile, SessionPeriod period, SessionStatus sessionStatus, EnrollmentRule enrollmentRule) { + this(null, imageFile, period, sessionStatus, enrollmentRule, new Enrollments()); + } + public Session(ImageFile imageFile, SessionPeriod period, SessionStatus sessionStatus, EnrollmentRule enrollmentRule, Enrollments enrollments) { this(null, imageFile, period, sessionStatus, enrollmentRule, enrollments); } diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java index d5352f1494..f87ca01a1b 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java @@ -10,6 +10,7 @@ import org.springframework.stereotype.Repository; import java.sql.PreparedStatement; +import java.util.List; @Repository("enrollmentRepository") public class JdbcEnrollmentRepository implements EnrollmentRepository { @@ -47,4 +48,19 @@ public Enrollment findById(Long id) { return jdbcTemplate.queryForObject(sql, rowMapper, id); } + + @Override + public List findBySessionId(Long sessionId) { + String sql = "select * from enrollment where session_id = ?"; + + RowMapper rowMapper = (rs, rowNum) -> new Enrollment( + rs.getLong("id"), + rs.getLong("student_id"), + rs.getLong("session_id") + ); + + return jdbcTemplate.query(sql, rowMapper, sessionId); + } + + } diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java index 84121ff0bf..5dc487b1e7 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java @@ -61,7 +61,6 @@ private static void extractedPrice(Session session, PreparedStatement ps) throws } } - @Override public Session findById(long id) { String sql = "select " + "s.id, " + @@ -80,73 +79,6 @@ public Session findById(long id) { "on s.image_id = i.id " + "where s.id = ?"; - return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> { - - // 1️⃣ ImageFile (지금은 ID만 복원) - ImageFile imageFile = new ImageFile( - rs.getLong("image_id"), - rs.getLong("size"), - rs.getString("image_type"), - rs.getInt("width"), - rs.getInt("height") - ); - - // 2️⃣ SessionPeriod - SessionPeriod period = new SessionPeriod( - rs.getObject("start_time", LocalDateTime.class), - rs.getObject("end_time", LocalDateTime.class) - ); - - // 3️⃣ SessionStatus - SessionStatus status = - SessionStatus.valueOf(rs.getString("session_status")); - - // 4️⃣ EnrollmentRule - Integer price = rs.getObject("price", Integer.class); - - EnrollmentRule enrollmentRule; - if (price != null) { - enrollmentRule = new PaidEnrollmentRule( - price, - rs.getInt("capacity") - ); - } else { - enrollmentRule = new FreeEnrollmentRule(); - } - - // 5️⃣ Enrollments (조회 시점에서는 비어 있음) - Enrollments enrollments = new Enrollments(); - - return new Session( - rs.getLong("id"), - imageFile, - period, - status, - enrollmentRule, - enrollments - ); - }, id); - } - - - public Session findById2(long id) { - String sql = "select " + - "s.id, " + - "s.session_status, " + - "s.price, " + - "s.capacity, " + - "s.start_time, " + - "s.end_time, " + - "i.id AS image_id, " + - "i.size, " + - "i.image_type, " + - "i.width, " + - "i.height " + - "from session s " + - "join image_file i " + - "on s.image_id = i.id " + - "where s.id = ?"; - return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> { ImageFile imageFile = new ImageFile( diff --git a/src/main/java/nextstep/courses/repository/EnrollmentRepository.java b/src/main/java/nextstep/courses/repository/EnrollmentRepository.java index 5527962a9e..95a700cde6 100644 --- a/src/main/java/nextstep/courses/repository/EnrollmentRepository.java +++ b/src/main/java/nextstep/courses/repository/EnrollmentRepository.java @@ -2,8 +2,12 @@ import nextstep.courses.domain.Enrollment; +import java.util.List; + public interface EnrollmentRepository { Long save(Enrollment enrollment); Enrollment findById(Long id); + + List findBySessionId(Long sessionId); } diff --git a/src/main/java/nextstep/courses/service/SessionService.java b/src/main/java/nextstep/courses/service/SessionService.java new file mode 100644 index 0000000000..070dc3ac9c --- /dev/null +++ b/src/main/java/nextstep/courses/service/SessionService.java @@ -0,0 +1,53 @@ +package nextstep.courses.service; + +import nextstep.courses.domain.*; +import nextstep.courses.repository.EnrollmentRepository; +import nextstep.courses.repository.ImageFileRepository; +import nextstep.courses.repository.SessionRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class SessionService { + + private final SessionRepository sessionRepository; + private final ImageFileRepository imageFileRepository; + private final EnrollmentRepository enrollmentRepository; + + public SessionService(SessionRepository sessionRepository, + ImageFileRepository imageFileRepository, + EnrollmentRepository enrollmentRepository) { + this.sessionRepository = sessionRepository; + this.imageFileRepository = imageFileRepository; + this.enrollmentRepository = enrollmentRepository; + } + + @Transactional + public Long createSession(ImageFile imageFile, + SessionPeriod period, + SessionStatus status, + EnrollmentRule enrollmentRule) { + + imageFileRepository.save(imageFile); + + Session session = new Session(imageFile, period, status, enrollmentRule); + + return sessionRepository.save(session); + } + + @Transactional(readOnly = true) + public Session findSession(Long id) { + return sessionRepository.findById(id); + } + + @Transactional + public void enroll(Long sessionId, Long memberId, Money money) { + Session session = sessionRepository.findById(sessionId); + + Enrollment enrollment = new Enrollment(memberId, sessionId); + + session.enroll(enrollment, money); + + enrollmentRepository.save(enrollment); + } +} diff --git a/src/test/java/nextstep/courses/domain/SessionBuilder.java b/src/test/java/nextstep/courses/domain/SessionBuilder.java index 92f7d4fe23..4778d17670 100644 --- a/src/test/java/nextstep/courses/domain/SessionBuilder.java +++ b/src/test/java/nextstep/courses/domain/SessionBuilder.java @@ -4,7 +4,7 @@ public class SessionBuilder { private Long id = 1L; - private ImageFile imageFile = new ImageFile(1024 * 1024); + private ImageFile imageFile = new ImageFile(1024 * 1024, "png", 300 , 200); private SessionPeriod period = new SessionPeriod(LocalDateTime.now(), LocalDateTime.now().plusDays(7)); private SessionStatus sessionStatus = SessionStatus.RECRUITING; diff --git a/src/test/java/nextstep/courses/repository/ImageFileRepositoryTest.java b/src/test/java/nextstep/courses/repository/ImageFileRepositoryTest.java index 6f23f36b39..af5b07e6ea 100644 --- a/src/test/java/nextstep/courses/repository/ImageFileRepositoryTest.java +++ b/src/test/java/nextstep/courses/repository/ImageFileRepositoryTest.java @@ -1,8 +1,6 @@ package nextstep.courses.repository; -import nextstep.courses.domain.Enrollment; import nextstep.courses.domain.ImageFile; -import nextstep.courses.domain.ImageType; import nextstep.courses.infrastructure.JdbcImageFileRepository; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/test/java/nextstep/courses/repository/SessionRepositoryTest.java b/src/test/java/nextstep/courses/repository/SessionRepositoryTest.java index c97f7ed4b9..c6506c072b 100644 --- a/src/test/java/nextstep/courses/repository/SessionRepositoryTest.java +++ b/src/test/java/nextstep/courses/repository/SessionRepositoryTest.java @@ -3,7 +3,6 @@ import nextstep.courses.domain.*; import nextstep.courses.infrastructure.JdbcImageFileRepository; import nextstep.courses.infrastructure.JdbcSessionRepository; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; @@ -11,7 +10,7 @@ import java.time.LocalDateTime; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; @JdbcTest @Import({JdbcSessionRepository.class, JdbcImageFileRepository.class}) @@ -50,9 +49,10 @@ void find() { Session session = new Session(imageFile, period, sessionStatus, enrollmentRule, enrollments); + Long sessionId = jdbcSessionRepository.save(session); - Session found = jdbcSessionRepository.findById2(sessionId); + Session found = jdbcSessionRepository.findById(sessionId); assertThat(found.getSessionStatus()).isEqualTo(session.getSessionStatus()); assertThat(found.getPeriod()).isEqualTo(session.getPeriod()); diff --git a/src/test/java/nextstep/courses/service/SessionServiceTest.java b/src/test/java/nextstep/courses/service/SessionServiceTest.java new file mode 100644 index 0000000000..ec63540d82 --- /dev/null +++ b/src/test/java/nextstep/courses/service/SessionServiceTest.java @@ -0,0 +1,49 @@ +package nextstep.courses.service; + +import nextstep.courses.domain.*; +import nextstep.courses.repository.EnrollmentRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +@Transactional +class SessionServiceTest { + + @Autowired + SessionService sessionService; + + @Autowired + EnrollmentRepository enrollmentRepository; + + @Test + void 강의를_개설하고_조회하기() { + ImageFile imageFile = new ImageFile(1024 * 1024, "png", 300, 200); + SessionPeriod period = new SessionPeriod(LocalDateTime.now(), LocalDateTime.now().plusDays(7)); + + + Long sessionId = sessionService.createSession(imageFile, period, SessionStatus.RECRUITING, new PaidEnrollmentRule(50000, 10)); + + Session found = sessionService.findSession(sessionId); + + assertThat(found.getSessionStatus()) + .isEqualTo(SessionStatus.RECRUITING.toString()); + assertThat(found.getPeriod()).isEqualTo(period); + } + + @Test + void 수강신청_성공() { + ImageFile imageFile = new ImageFile(1024 * 1024, "png", 300, 200); + SessionPeriod period = new SessionPeriod(LocalDateTime.now(), LocalDateTime.now().plusDays(7)); + Long sessionId = sessionService.createSession(imageFile, period, SessionStatus.RECRUITING, new PaidEnrollmentRule(50000, 10)); + + sessionService.enroll(sessionId, 10L, new Money(50000)); + + assertThat(enrollmentRepository.findBySessionId(sessionId)).hasSize(1); + } +} \ No newline at end of file