diff --git a/src/main/java/nextstep/courses/domain/Course.java b/src/main/java/nextstep/courses/domain/Course.java index f664a63a82..26921d8b5a 100644 --- a/src/main/java/nextstep/courses/domain/Course.java +++ b/src/main/java/nextstep/courses/domain/Course.java @@ -1,5 +1,8 @@ package nextstep.courses.domain; +import nextstep.courses.domain.session.Session; +import nextstep.courses.domain.session.Sessions; + import java.time.LocalDateTime; public class Course { diff --git a/src/main/java/nextstep/courses/domain/CoverImage.java b/src/main/java/nextstep/courses/domain/CoverImage.java deleted file mode 100644 index e95702300f..0000000000 --- a/src/main/java/nextstep/courses/domain/CoverImage.java +++ /dev/null @@ -1,17 +0,0 @@ -package nextstep.courses.domain; - -public class CoverImage { - private ImageSize imageSize; - private ImageType imageType; - private ImageDimension imageDimension; - - public CoverImage(int size, String fileName, int width, int height) { - this(new ImageSize(size), ImageType.fromFileName(fileName), new ImageDimension(width, height)); - } - - private CoverImage(ImageSize imageSize, ImageType imageType, ImageDimension imageDimension) { - this.imageSize = imageSize; - this.imageType = imageType; - this.imageDimension = imageDimension; - } -} diff --git a/src/main/java/nextstep/courses/domain/Enrollment.java b/src/main/java/nextstep/courses/domain/Enrollment.java deleted file mode 100644 index 9cb613487f..0000000000 --- a/src/main/java/nextstep/courses/domain/Enrollment.java +++ /dev/null @@ -1,19 +0,0 @@ -package nextstep.courses.domain; - -import java.time.LocalDateTime; - -public class Enrollment { - private final Long sessionId; - private final Long userId; - private final LocalDateTime enrollmentDate; - - public Enrollment(Long sessionId, Long userId) { - this.sessionId = sessionId; - this.userId = userId; - this.enrollmentDate = LocalDateTime.now(); - } - - public boolean isSameUser(Long userId) { - return this.userId.equals(userId); - } -} diff --git a/src/main/java/nextstep/courses/domain/Session.java b/src/main/java/nextstep/courses/domain/Session.java deleted file mode 100644 index 99f96191df..0000000000 --- a/src/main/java/nextstep/courses/domain/Session.java +++ /dev/null @@ -1,48 +0,0 @@ -package nextstep.courses.domain; - -import nextstep.payments.domain.Payment; - -import java.time.LocalDateTime; - -public class Session { - private final long id; - private final SessionDuration sessionDuration; - private final CoverImage coverImage; - private final EnrollmentPolicy enrollmentPolicy; - private final SessionState sessionState; - private final Enrollments enrollments; - - public Session(long id - , LocalDateTime startDate - , LocalDateTime endDate - , int size - , String fileName - , int width - , int height - , EnrollmentPolicy enrollmentPolicy - , SessionState sessionState - , Enrollments enrollments) { - this(id, new SessionDuration(startDate, endDate), new CoverImage(size, fileName, width, height) - , enrollmentPolicy, sessionState, enrollments); - } - - public Session(long id, SessionDuration sessionDuration, CoverImage coverImage - , EnrollmentPolicy enrollmentPolicy, SessionState sessionState, Enrollments enrollments) { - this.id = id; - this.sessionDuration = sessionDuration; - this.coverImage = coverImage; - this.enrollmentPolicy = enrollmentPolicy; - this.sessionState = sessionState; - this.enrollments = enrollments; - } - - public Enrollment enroll(Long userId, Payment payment) { - sessionState.validateEnroll(); - enrollmentPolicy.validateEnrollment(payment); - return enrollments.add(this.id, userId); - } - - public long getId() { - return id; - } -} diff --git a/src/main/java/nextstep/courses/domain/Capacity.java b/src/main/java/nextstep/courses/domain/enrollment/Capacity.java similarity index 82% rename from src/main/java/nextstep/courses/domain/Capacity.java rename to src/main/java/nextstep/courses/domain/enrollment/Capacity.java index 66ab224c67..40328e7425 100644 --- a/src/main/java/nextstep/courses/domain/Capacity.java +++ b/src/main/java/nextstep/courses/domain/enrollment/Capacity.java @@ -1,4 +1,4 @@ -package nextstep.courses.domain; +package nextstep.courses.domain.enrollment; public class Capacity { @@ -23,4 +23,8 @@ public void validateAvailable() { public void increase() { this.current++; } + + public int value() { + return max; + } } diff --git a/src/main/java/nextstep/courses/domain/enrollment/Enrollment.java b/src/main/java/nextstep/courses/domain/enrollment/Enrollment.java new file mode 100644 index 0000000000..79edad7a58 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/enrollment/Enrollment.java @@ -0,0 +1,36 @@ +package nextstep.courses.domain.enrollment; + +import java.time.LocalDateTime; + +public class Enrollment { + private final Long sessionId; + private final Long userId; + private final LocalDateTime enrollmentDate; + + public Enrollment(Long sessionId, Long userId) { + this(sessionId, userId, LocalDateTime.now()); + } + + public Enrollment(Long sessionId, Long userId, LocalDateTime enrollmentDate) { + this.sessionId = sessionId; + this.userId = userId; + this.enrollmentDate = enrollmentDate; + } + + public boolean isSameUser(Long userId) { + return this.userId.equals(userId); + } + + public Long getSessionId() { + return sessionId; + } + + + public Long getUserId() { + return userId; + } + + public LocalDateTime getEnrollmentDate() { + return enrollmentDate; + } +} diff --git a/src/main/java/nextstep/courses/domain/EnrollmentPolicy.java b/src/main/java/nextstep/courses/domain/enrollment/EnrollmentPolicy.java similarity index 59% rename from src/main/java/nextstep/courses/domain/EnrollmentPolicy.java rename to src/main/java/nextstep/courses/domain/enrollment/EnrollmentPolicy.java index ee8b0773e7..2392a505ea 100644 --- a/src/main/java/nextstep/courses/domain/EnrollmentPolicy.java +++ b/src/main/java/nextstep/courses/domain/enrollment/EnrollmentPolicy.java @@ -1,7 +1,9 @@ -package nextstep.courses.domain; +package nextstep.courses.domain.enrollment; import nextstep.payments.domain.Payment; public interface EnrollmentPolicy { + PolicyType type(); + Long price(); void validateEnrollment(Payment payment); } diff --git a/src/main/java/nextstep/courses/domain/enrollment/EnrollmentPolicyFactory.java b/src/main/java/nextstep/courses/domain/enrollment/EnrollmentPolicyFactory.java new file mode 100644 index 0000000000..3a669fdef6 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/enrollment/EnrollmentPolicyFactory.java @@ -0,0 +1,20 @@ +package nextstep.courses.domain.enrollment; + +import static nextstep.courses.domain.enrollment.PolicyType.FREE; +import static nextstep.courses.domain.enrollment.PolicyType.PAID; + +public class EnrollmentPolicyFactory { + public static EnrollmentPolicy create(String name, Long price) { + PolicyType type = PolicyType.valueOf(name); + + if (type == FREE) { + return new FreeEnrollmentPolicy(); + } + + if (type == PAID) { + return new PaidEnrollmentPolicy(new Money(price)); + } + + throw new IllegalArgumentException(); + } +} diff --git a/src/main/java/nextstep/courses/domain/Enrollments.java b/src/main/java/nextstep/courses/domain/enrollment/Enrollments.java similarity index 84% rename from src/main/java/nextstep/courses/domain/Enrollments.java rename to src/main/java/nextstep/courses/domain/enrollment/Enrollments.java index f96555260c..904c732405 100644 --- a/src/main/java/nextstep/courses/domain/Enrollments.java +++ b/src/main/java/nextstep/courses/domain/enrollment/Enrollments.java @@ -1,4 +1,4 @@ -package nextstep.courses.domain; +package nextstep.courses.domain.enrollment; import java.util.ArrayList; import java.util.List; @@ -25,7 +25,7 @@ public void validateEnroll(Long userId) { capacity.validateAvailable(); } - public Enrollment add(Long sessionId, Long userId) { + public Enrollment enroll(Long sessionId, Long userId) { validateEnroll(userId); Enrollment enrollment = new Enrollment(sessionId, userId); @@ -39,6 +39,10 @@ private boolean EnrolledCheck(Long userId) { return enrollments.stream() .anyMatch(enrollment -> enrollment.isSameUser(userId)); } + + public int getCapacity() { + return capacity.value(); + } } diff --git a/src/main/java/nextstep/courses/domain/FreeEnrollmentPolicy.java b/src/main/java/nextstep/courses/domain/enrollment/FreeEnrollmentPolicy.java similarity index 54% rename from src/main/java/nextstep/courses/domain/FreeEnrollmentPolicy.java rename to src/main/java/nextstep/courses/domain/enrollment/FreeEnrollmentPolicy.java index 21720b9ec7..64d902c1c4 100644 --- a/src/main/java/nextstep/courses/domain/FreeEnrollmentPolicy.java +++ b/src/main/java/nextstep/courses/domain/enrollment/FreeEnrollmentPolicy.java @@ -1,8 +1,18 @@ -package nextstep.courses.domain; +package nextstep.courses.domain.enrollment; import nextstep.payments.domain.Payment; public class FreeEnrollmentPolicy implements EnrollmentPolicy{ + @Override + public PolicyType type() { + return PolicyType.FREE; + } + + @Override + public Long price() { + return null; + } + @Override public void validateEnrollment(Payment payment) { // 무료강의는 검증하지 않는다 diff --git a/src/main/java/nextstep/courses/domain/Money.java b/src/main/java/nextstep/courses/domain/enrollment/Money.java similarity index 85% rename from src/main/java/nextstep/courses/domain/Money.java rename to src/main/java/nextstep/courses/domain/enrollment/Money.java index bd571b378f..33dd6087a1 100644 --- a/src/main/java/nextstep/courses/domain/Money.java +++ b/src/main/java/nextstep/courses/domain/enrollment/Money.java @@ -1,4 +1,4 @@ -package nextstep.courses.domain; +package nextstep.courses.domain.enrollment; import java.util.Objects; @@ -13,6 +13,10 @@ public boolean isEqualTo(Money other) { return this.equals(other); } + public long value() { + return amount; + } + @Override public boolean equals(Object object) { if (this == object) return true; diff --git a/src/main/java/nextstep/courses/domain/PaidEnrollmentPolicy.java b/src/main/java/nextstep/courses/domain/enrollment/PaidEnrollmentPolicy.java similarity index 70% rename from src/main/java/nextstep/courses/domain/PaidEnrollmentPolicy.java rename to src/main/java/nextstep/courses/domain/enrollment/PaidEnrollmentPolicy.java index 4d700fa689..8f0e2aa0e4 100644 --- a/src/main/java/nextstep/courses/domain/PaidEnrollmentPolicy.java +++ b/src/main/java/nextstep/courses/domain/enrollment/PaidEnrollmentPolicy.java @@ -1,4 +1,4 @@ -package nextstep.courses.domain; +package nextstep.courses.domain.enrollment; import nextstep.payments.domain.Payment; @@ -10,6 +10,16 @@ public PaidEnrollmentPolicy(Money money) { this.money = money; } + @Override + public PolicyType type() { + return PolicyType.PAID; + } + + @Override + public Long price() { + return money.value(); + } + @Override public void validateEnrollment(Payment payment) { validatePayment(payment); diff --git a/src/main/java/nextstep/courses/domain/enrollment/PolicyType.java b/src/main/java/nextstep/courses/domain/enrollment/PolicyType.java new file mode 100644 index 0000000000..130443cf96 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/enrollment/PolicyType.java @@ -0,0 +1,6 @@ +package nextstep.courses.domain.enrollment; + +public enum PolicyType { + FREE, + PAID +} diff --git a/src/main/java/nextstep/courses/domain/session/Session.java b/src/main/java/nextstep/courses/domain/session/Session.java new file mode 100644 index 0000000000..7e8a83560d --- /dev/null +++ b/src/main/java/nextstep/courses/domain/session/Session.java @@ -0,0 +1,112 @@ +package nextstep.courses.domain.session; + +import nextstep.courses.domain.enrollment.Enrollment; +import nextstep.courses.domain.enrollment.EnrollmentPolicy; +import nextstep.courses.domain.enrollment.Enrollments; +import nextstep.courses.domain.session.cover.CoverImage; +import nextstep.payments.domain.Payment; + +import java.time.LocalDateTime; + +public class Session { + private final long id; + private final long courseId; + private final SessionDuration sessionDuration; + private final CoverImage coverImage; + private final EnrollmentPolicy enrollmentPolicy; + private final SessionState sessionState; + private final Enrollments enrollments; + + public Session(long id + , long courseId + , LocalDateTime startDate + , LocalDateTime endDate + , int size + , String fileName + , int width + , int height + , EnrollmentPolicy enrollmentPolicy + , SessionState sessionState + , Enrollments enrollments) { + + this(id, courseId, new SessionDuration(startDate, endDate), new CoverImage(size, fileName, width, height) + , enrollmentPolicy, sessionState, enrollments); + } + + public Session(long id, long courseId, SessionDuration sessionDuration, CoverImage coverImage + , EnrollmentPolicy enrollmentPolicy, SessionState sessionState, Enrollments enrollments) { + this.id = id; + this.courseId = courseId; + this.sessionDuration = sessionDuration; + this.coverImage = coverImage; + this.enrollmentPolicy = enrollmentPolicy; + this.sessionState = sessionState; + this.enrollments = enrollments; + } + + public Enrollment enroll(Long userId, Payment payment) { + sessionState.validateEnroll(); + enrollmentPolicy.validateEnrollment(payment); + return enrollments.enroll(this.id, userId); + } + + public long getId() { + return id; + } + + public long getCourseId() { + return courseId; + } + + public LocalDateTime getStartDate() { + return sessionDuration.getStartDate(); + } + + public LocalDateTime getEndDate() { + return sessionDuration.getEndDate(); + } + + public int getCoverImageSize() { + return coverImage.getImageSize(); + } + + public String getCoverImageName() { + return coverImage.getImageName(); + } + + public int getCoverImageWidth() { + return coverImage.getCoverImageWidth(); + } + + public int getCoverImageHeight() { + return coverImage.getCoverImageHeight(); + } + + public String getPolicyType() { + return enrollmentPolicy.type().name(); + } + + public long getPrice() { + return enrollmentPolicy.price(); + } + + public String getState() { + return sessionState.name(); + } + + public int getCapacity() { + return enrollments.getCapacity(); + } + + @Override + public String toString() { + return "Session{" + + "id=" + id + + ", sessionDuration=" + sessionDuration + + ", coverImage=" + coverImage + + ", enrollmentPolicy=" + enrollmentPolicy + + ", sessionState=" + sessionState + + ", enrollments=" + enrollments + + '}'; + } +} diff --git a/src/main/java/nextstep/courses/domain/SessionDuration.java b/src/main/java/nextstep/courses/domain/session/SessionDuration.java similarity index 73% rename from src/main/java/nextstep/courses/domain/SessionDuration.java rename to src/main/java/nextstep/courses/domain/session/SessionDuration.java index 54d7714a2c..52dca15785 100644 --- a/src/main/java/nextstep/courses/domain/SessionDuration.java +++ b/src/main/java/nextstep/courses/domain/session/SessionDuration.java @@ -1,4 +1,4 @@ -package nextstep.courses.domain; +package nextstep.courses.domain.session; import java.time.LocalDateTime; @@ -17,4 +17,12 @@ private void validateDate(LocalDateTime startDate, LocalDateTime endDate) { throw new IllegalArgumentException(); } } + + public LocalDateTime getStartDate() { + return startDate; + } + + public LocalDateTime getEndDate() { + return endDate; + } } diff --git a/src/main/java/nextstep/courses/domain/SessionState.java b/src/main/java/nextstep/courses/domain/session/SessionState.java similarity index 82% rename from src/main/java/nextstep/courses/domain/SessionState.java rename to src/main/java/nextstep/courses/domain/session/SessionState.java index e087309d58..a7d1100fa8 100644 --- a/src/main/java/nextstep/courses/domain/SessionState.java +++ b/src/main/java/nextstep/courses/domain/session/SessionState.java @@ -1,4 +1,4 @@ -package nextstep.courses.domain; +package nextstep.courses.domain.session; public enum SessionState { READY, diff --git a/src/main/java/nextstep/courses/domain/Sessions.java b/src/main/java/nextstep/courses/domain/session/Sessions.java similarity index 90% rename from src/main/java/nextstep/courses/domain/Sessions.java rename to src/main/java/nextstep/courses/domain/session/Sessions.java index f8c04a7741..108159c2b6 100644 --- a/src/main/java/nextstep/courses/domain/Sessions.java +++ b/src/main/java/nextstep/courses/domain/session/Sessions.java @@ -1,4 +1,4 @@ -package nextstep.courses.domain; +package nextstep.courses.domain.session; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/nextstep/courses/domain/session/cover/CoverImage.java b/src/main/java/nextstep/courses/domain/session/cover/CoverImage.java new file mode 100644 index 0000000000..c16060eaef --- /dev/null +++ b/src/main/java/nextstep/courses/domain/session/cover/CoverImage.java @@ -0,0 +1,35 @@ +package nextstep.courses.domain.session.cover; + +public class CoverImage { + private final ImageSize imageSize; + private final String fileName; + private final ImageType imageType; + private final ImageDimension imageDimension; + + public CoverImage(int size, String fileName, int width, int height) { + this(new ImageSize(size), fileName, ImageType.fromFileName(fileName), new ImageDimension(width, height)); + } + + private CoverImage(ImageSize imageSize, String fileName, ImageType imageType, ImageDimension imageDimension) { + this.imageSize = imageSize; + this.fileName = fileName; + this.imageType = imageType; + this.imageDimension = imageDimension; + } + + public int getImageSize() { + return imageSize.value(); + } + + public String getImageName() { + return fileName; + } + + public int getCoverImageWidth() { + return imageDimension.width(); + } + + public int getCoverImageHeight() { + return imageDimension.height(); + } +} diff --git a/src/main/java/nextstep/courses/domain/ImageDimension.java b/src/main/java/nextstep/courses/domain/session/cover/ImageDimension.java similarity index 84% rename from src/main/java/nextstep/courses/domain/ImageDimension.java rename to src/main/java/nextstep/courses/domain/session/cover/ImageDimension.java index 806de0874b..276a7d43c3 100644 --- a/src/main/java/nextstep/courses/domain/ImageDimension.java +++ b/src/main/java/nextstep/courses/domain/session/cover/ImageDimension.java @@ -1,4 +1,4 @@ -package nextstep.courses.domain; +package nextstep.courses.domain.session.cover; public class ImageDimension { @@ -27,4 +27,12 @@ private static void validateRatio(int width, int height) { throw new IllegalArgumentException(); } } + + public int width() { + return width; + } + + public int height() { + return height; + } } diff --git a/src/main/java/nextstep/courses/domain/ImageSize.java b/src/main/java/nextstep/courses/domain/session/cover/ImageSize.java similarity index 86% rename from src/main/java/nextstep/courses/domain/ImageSize.java rename to src/main/java/nextstep/courses/domain/session/cover/ImageSize.java index b03838043b..32bd4a15a8 100644 --- a/src/main/java/nextstep/courses/domain/ImageSize.java +++ b/src/main/java/nextstep/courses/domain/session/cover/ImageSize.java @@ -1,4 +1,4 @@ -package nextstep.courses.domain; +package nextstep.courses.domain.session.cover; import java.util.Objects; @@ -26,4 +26,8 @@ public boolean equals(Object object) { public int hashCode() { return Objects.hashCode(imageSize); } + + public int value() { + return imageSize; + } } diff --git a/src/main/java/nextstep/courses/domain/ImageType.java b/src/main/java/nextstep/courses/domain/session/cover/ImageType.java similarity index 95% rename from src/main/java/nextstep/courses/domain/ImageType.java rename to src/main/java/nextstep/courses/domain/session/cover/ImageType.java index acaac0063f..2fc7ac181d 100644 --- a/src/main/java/nextstep/courses/domain/ImageType.java +++ b/src/main/java/nextstep/courses/domain/session/cover/ImageType.java @@ -1,4 +1,4 @@ -package nextstep.courses.domain; +package nextstep.courses.domain.session.cover; import java.util.Arrays; diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java index 44fedde4c3..429aada650 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java @@ -1,7 +1,9 @@ package nextstep.courses.infrastructure; import nextstep.courses.domain.Course; -import nextstep.courses.domain.CourseRepository; +import nextstep.courses.domain.session.Sessions; +import nextstep.courses.repository.CourseRepository; +import nextstep.courses.repository.SessionRepository; import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; @@ -12,9 +14,11 @@ @Repository("courseRepository") public class JdbcCourseRepository implements CourseRepository { private JdbcOperations jdbcTemplate; + private final SessionRepository sessionRepository; - public JdbcCourseRepository(JdbcOperations jdbcTemplate) { + public JdbcCourseRepository(JdbcOperations jdbcTemplate, SessionRepository sessionRepository) { this.jdbcTemplate = jdbcTemplate; + this.sessionRepository = sessionRepository; } @Override @@ -30,7 +34,7 @@ public Course findById(Long id) { rs.getLong(1), rs.getString(2), rs.getLong(3), - null, + new Sessions(sessionRepository.findByCourseId(id)), toLocalDateTime(rs.getTimestamp(4)), toLocalDateTime(rs.getTimestamp(5))); return jdbcTemplate.queryForObject(sql, rowMapper, id); 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..da4571143e --- /dev/null +++ b/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java @@ -0,0 +1,45 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.enrollment.Enrollment; +import nextstep.courses.repository.EnrollmentRepository; +import org.springframework.jdbc.core.JdbcOperations; +import org.springframework.stereotype.Repository; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.List; + +@Repository +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 (session_id, user_id, enrolled_at) VALUES (?, ?, ?)"; + return jdbcTemplate.update(sql, enrollment.getSessionId(), enrollment.getUserId(), enrollment.getEnrollmentDate()); + } + + @Override + public List findBySessionId(Long sessionId) { + String sql = "SELECT session_id, user_id, enrolled_at FROM enrollment WHERE session_id = ?"; + + return jdbcTemplate.query(sql, (rs, rowNum) -> + new Enrollment( + rs.getLong("session_id"), + rs.getLong("user_id"), + toLocalDateTime(rs.getTimestamp("enrolled_at")) + ), sessionId + ); + } + + private LocalDateTime toLocalDateTime(Timestamp timestamp) { + if (timestamp == null) { + return null; + } + return timestamp.toLocalDateTime(); + } +} 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..f7319e93da --- /dev/null +++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java @@ -0,0 +1,84 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.enrollment.Capacity; +import nextstep.courses.domain.enrollment.EnrollmentPolicyFactory; +import nextstep.courses.domain.enrollment.Enrollments; +import nextstep.courses.domain.session.Session; +import nextstep.courses.domain.session.SessionState; +import nextstep.courses.repository.SessionRepository; +import org.springframework.jdbc.core.JdbcOperations; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.List; + +@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 (id, course_id, start_at, end_at, cover_image_size, cover_image_name, cover_image_width, cover_image_height, policy_type," + + "price, state, capacity, created_at, updated_at) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + return jdbcTemplate.update(sql, session.getId(), session.getCourseId(), session.getStartDate(), session.getEndDate(), session.getCoverImageSize(), session.getCoverImageName() + , session.getCoverImageWidth(), session.getCoverImageHeight(), session.getPolicyType(), session.getPrice(), session.getState(), session.getCapacity() + , LocalDateTime.now(), LocalDateTime.now()); + } + + @Override + public Session findById(Long id) { + String sql = "SELECT id, course_id, start_at, end_at, cover_image_size, cover_image_name, " + + "cover_image_width, cover_image_height, policy_type, price, state, capacity " + + "FROM session WHERE id = ?"; + + RowMapper rowMapper = (rs, rowNum) -> new Session( + rs.getLong("id"), + rs.getLong("course_id"), + toLocalDateTime(rs.getTimestamp("start_at")), + toLocalDateTime(rs.getTimestamp("end_at")), + rs.getInt("cover_image_size"), + rs.getString("cover_image_name"), + rs.getInt("cover_image_width"), + rs.getInt("cover_image_height"), + EnrollmentPolicyFactory.create(rs.getString("policy_type"), rs.getLong("price")), + SessionState.valueOf(rs.getString("state")), + new Enrollments(new Capacity(rs.getInt("capacity"))) + ); + + return jdbcTemplate.queryForObject(sql, rowMapper, id); + } + + @Override + public List findByCourseId(Long courseId) { + String sql = "SELECT id, course_id, start_at, end_at, cover_image_size, cover_image_name, " + + "cover_image_width, cover_image_height, policy_type, price, state, capacity " + + "FROM session WHERE course_id = ?"; + RowMapper rowMapper = (rs, rowNum) -> new Session( + rs.getLong("id"), + rs.getLong("course_id"), + toLocalDateTime(rs.getTimestamp("start_at")), + toLocalDateTime(rs.getTimestamp("end_at")), + rs.getInt("cover_image_size"), + rs.getString("cover_image_name"), + rs.getInt("cover_image_width"), + rs.getInt("cover_image_height"), + EnrollmentPolicyFactory.create(rs.getString("policy_type"), rs.getLong("price")), + SessionState.valueOf(rs.getString("state")), + new Enrollments(new Capacity(rs.getInt("capacity"))) + ); + return jdbcTemplate.query(sql, rowMapper, courseId); + } + + private LocalDateTime toLocalDateTime(Timestamp timestamp) { + if (timestamp == null) { + return null; + } + return timestamp.toLocalDateTime(); + } +} 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..186b6be856 --- /dev/null +++ b/src/main/java/nextstep/courses/repository/EnrollmentRepository.java @@ -0,0 +1,10 @@ +package nextstep.courses.repository; + +import nextstep.courses.domain.enrollment.Enrollment; + +import java.util.List; + +public interface EnrollmentRepository { + int save(Enrollment enrollment); + List findBySessionId(Long sessionId); +} 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..9446af2a75 --- /dev/null +++ b/src/main/java/nextstep/courses/repository/SessionRepository.java @@ -0,0 +1,13 @@ +package nextstep.courses.repository; + +import nextstep.courses.domain.session.Session; + +import java.util.List; + +public interface SessionRepository { + int save(Session session); + + Session findById(Long id); + + List findByCourseId(Long courseId); +} 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..ab41766893 --- /dev/null +++ b/src/main/java/nextstep/courses/service/SessionService.java @@ -0,0 +1,28 @@ +package nextstep.courses.service; + +import nextstep.courses.domain.enrollment.Enrollment; +import nextstep.courses.domain.session.Session; +import nextstep.courses.repository.EnrollmentRepository; +import nextstep.courses.repository.SessionRepository; +import nextstep.payments.domain.Payment; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional +public class SessionService { + + private final SessionRepository sessionRepository; + private final EnrollmentRepository enrollmentRepository; + + public SessionService(SessionRepository sessionRepository, EnrollmentRepository enrollmentRepository) { + this.sessionRepository = sessionRepository; + this.enrollmentRepository = enrollmentRepository; + } + + public void enroll(Long sessionId, Long userId, Payment payment) { + Session session = sessionRepository.findById(sessionId); + Enrollment enrollment = session.enroll(userId, payment); + enrollmentRepository.save(enrollment); + } +} diff --git a/src/main/java/nextstep/payments/domain/Payment.java b/src/main/java/nextstep/payments/domain/Payment.java index 6d4c2b720c..da120d6f75 100644 --- a/src/main/java/nextstep/payments/domain/Payment.java +++ b/src/main/java/nextstep/payments/domain/Payment.java @@ -1,6 +1,6 @@ package nextstep.payments.domain; -import nextstep.courses.domain.Money; +import nextstep.courses.domain.enrollment.Money; import java.time.LocalDateTime; diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 8d5a988c8b..fb1bec44ec 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -48,3 +48,29 @@ create table delete_history ( deleted_by_id bigint, primary key (id) ); + +create table session ( + id bigint generated by default as identity, + course_id bigint not null, + start_at timestamp not null, + end_at timestamp not null, + cover_image_size varchar(20), + cover_image_name varchar(20), + cover_image_width int, + cover_image_height int, + policy_type varchar(20) not null, + price bigint, + state varchar(20), + capacity int not null, + created_at timestamp not null, + updated_at timestamp, + primary key (id) +); + +create table enrollment ( + id bigint generated by default as identity, + session_id bigint not null, + user_id bigint not null, + enrolled_at timestamp not null, + primary key (id) +); \ No newline at end of file diff --git a/src/test/java/nextstep/courses/domain/CapacityTest.java b/src/test/java/nextstep/courses/domain/CapacityTest.java index 9a72a4879e..9d6a98db3d 100644 --- a/src/test/java/nextstep/courses/domain/CapacityTest.java +++ b/src/test/java/nextstep/courses/domain/CapacityTest.java @@ -1,5 +1,6 @@ package nextstep.courses.domain; +import nextstep.courses.domain.enrollment.Capacity; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/nextstep/courses/domain/EnrollmentsTest.java b/src/test/java/nextstep/courses/domain/EnrollmentsTest.java index 6b8f42fee8..55dd2e4270 100644 --- a/src/test/java/nextstep/courses/domain/EnrollmentsTest.java +++ b/src/test/java/nextstep/courses/domain/EnrollmentsTest.java @@ -1,5 +1,7 @@ package nextstep.courses.domain; +import nextstep.courses.domain.enrollment.Capacity; +import nextstep.courses.domain.enrollment.Enrollments; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -7,9 +9,9 @@ class EnrollmentsTest { @Test void Enrollments_sameUserId() { Enrollments enrollments = new Enrollments(new Capacity(2)); - enrollments.add(1L, 10L); + enrollments.enroll(1L, 10L); - assertThatThrownBy(() -> enrollments.add(1L, 10L)) + assertThatThrownBy(() -> enrollments.enroll(1L, 10L)) .isInstanceOf(IllegalStateException.class); } } \ No newline at end of file diff --git a/src/test/java/nextstep/courses/domain/ImageDimensionTest.java b/src/test/java/nextstep/courses/domain/ImageDimensionTest.java index 1f3805d39a..94baa65953 100644 --- a/src/test/java/nextstep/courses/domain/ImageDimensionTest.java +++ b/src/test/java/nextstep/courses/domain/ImageDimensionTest.java @@ -1,5 +1,6 @@ package nextstep.courses.domain; +import nextstep.courses.domain.session.cover.ImageDimension; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/nextstep/courses/domain/ImageSizeTest.java b/src/test/java/nextstep/courses/domain/ImageSizeTest.java index 5685a14cdc..3ee4caeacb 100644 --- a/src/test/java/nextstep/courses/domain/ImageSizeTest.java +++ b/src/test/java/nextstep/courses/domain/ImageSizeTest.java @@ -1,9 +1,10 @@ package nextstep.courses.domain; +import nextstep.courses.domain.session.cover.ImageSize; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import static nextstep.courses.domain.ImageSize.MAX_SIZE; +import static nextstep.courses.domain.session.cover.ImageSize.MAX_SIZE; import static org.assertj.core.api.Assertions.*; class ImageSizeTest { diff --git a/src/test/java/nextstep/courses/domain/ImageTypeTest.java b/src/test/java/nextstep/courses/domain/ImageTypeTest.java index d3e2f7da26..2749d82258 100644 --- a/src/test/java/nextstep/courses/domain/ImageTypeTest.java +++ b/src/test/java/nextstep/courses/domain/ImageTypeTest.java @@ -1,5 +1,6 @@ package nextstep.courses.domain; +import nextstep.courses.domain.session.cover.ImageType; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/nextstep/courses/domain/MoneyTest.java b/src/test/java/nextstep/courses/domain/MoneyTest.java index 361ef9858a..67c1c0d9bc 100644 --- a/src/test/java/nextstep/courses/domain/MoneyTest.java +++ b/src/test/java/nextstep/courses/domain/MoneyTest.java @@ -1,6 +1,6 @@ package nextstep.courses.domain; -import org.assertj.core.api.Assertions; +import nextstep.courses.domain.enrollment.Money; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/nextstep/courses/domain/PaidEnrollmentPolicyTest.java b/src/test/java/nextstep/courses/domain/PaidEnrollmentPolicyTest.java index d38464a6ad..038668106c 100644 --- a/src/test/java/nextstep/courses/domain/PaidEnrollmentPolicyTest.java +++ b/src/test/java/nextstep/courses/domain/PaidEnrollmentPolicyTest.java @@ -1,5 +1,7 @@ package nextstep.courses.domain; +import nextstep.courses.domain.enrollment.Money; +import nextstep.courses.domain.enrollment.PaidEnrollmentPolicy; import nextstep.payments.domain.Payment; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/nextstep/courses/domain/PaymentTestBuilder.java b/src/test/java/nextstep/courses/domain/PaymentTestBuilder.java index 243cc39ddf..9866e95358 100644 --- a/src/test/java/nextstep/courses/domain/PaymentTestBuilder.java +++ b/src/test/java/nextstep/courses/domain/PaymentTestBuilder.java @@ -1,5 +1,7 @@ package nextstep.courses.domain; +import nextstep.courses.domain.enrollment.Money; +import nextstep.courses.domain.session.Session; import nextstep.payments.domain.Payment; public class PaymentTestBuilder { diff --git a/src/test/java/nextstep/courses/domain/SessionDurationTest.java b/src/test/java/nextstep/courses/domain/SessionDurationTest.java index fbc26915fe..fb5b87475c 100644 --- a/src/test/java/nextstep/courses/domain/SessionDurationTest.java +++ b/src/test/java/nextstep/courses/domain/SessionDurationTest.java @@ -1,5 +1,6 @@ package nextstep.courses.domain; +import nextstep.courses.domain.session.SessionDuration; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/nextstep/courses/domain/SessionStateTest.java b/src/test/java/nextstep/courses/domain/SessionStateTest.java index 85ac02fc3b..6c802960d1 100644 --- a/src/test/java/nextstep/courses/domain/SessionStateTest.java +++ b/src/test/java/nextstep/courses/domain/SessionStateTest.java @@ -1,5 +1,6 @@ package nextstep.courses.domain; +import nextstep.courses.domain.session.SessionState; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/nextstep/courses/domain/SessionTest.java b/src/test/java/nextstep/courses/domain/SessionTest.java index 324ffbf5ff..ca197bff14 100644 --- a/src/test/java/nextstep/courses/domain/SessionTest.java +++ b/src/test/java/nextstep/courses/domain/SessionTest.java @@ -1,5 +1,7 @@ package nextstep.courses.domain; +import nextstep.courses.domain.enrollment.Money; +import nextstep.courses.domain.session.Session; import nextstep.payments.domain.Payment; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/nextstep/courses/domain/SessionTestBuilder.java b/src/test/java/nextstep/courses/domain/SessionTestBuilder.java index cc9da4987e..9541c5c581 100644 --- a/src/test/java/nextstep/courses/domain/SessionTestBuilder.java +++ b/src/test/java/nextstep/courses/domain/SessionTestBuilder.java @@ -1,11 +1,19 @@ package nextstep.courses.domain; +import nextstep.courses.domain.enrollment.*; +import nextstep.courses.domain.session.Session; +import nextstep.courses.domain.session.SessionDuration; +import nextstep.courses.domain.session.SessionState; +import nextstep.courses.domain.session.cover.CoverImage; + import java.time.LocalDateTime; public class SessionTestBuilder { private long id = 1L; + private long courseId = 10L; + private SessionDuration sessionDuration = new SessionDuration( LocalDateTime.now().plusDays(1), LocalDateTime.now().plusDays(2) @@ -23,6 +31,16 @@ public static SessionTestBuilder aSession() { return new SessionTestBuilder(); } + public SessionTestBuilder withId(long id) { + this.id = id; + return this; + } + + public SessionTestBuilder withCourseId(long courseId) { + this.courseId = courseId; + return this; + } + public SessionTestBuilder withClosedSession() { this.sessionState = SessionState.CLOSED; return this; @@ -40,7 +58,7 @@ public SessionTestBuilder withCapacity(int max) { public SessionTestBuilder withFullEnrollments(int count) { for (int i = 0; i < count; i++) { - enrollments.add(this.id, (long) i + 1); + enrollments.enroll(this.id, (long) i + 1); } return this; } @@ -48,6 +66,7 @@ public SessionTestBuilder withFullEnrollments(int count) { public Session build() { return new Session( id, + courseId, sessionDuration, coverImage, enrollmentPolicy, diff --git a/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java index f087fc0ad2..c26feb07d4 100644 --- a/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java +++ b/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java @@ -1,7 +1,8 @@ package nextstep.courses.infrastructure; import nextstep.courses.domain.Course; -import nextstep.courses.domain.CourseRepository; +import nextstep.courses.repository.CourseRepository; +import nextstep.courses.repository.SessionRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; @@ -20,10 +21,12 @@ public class CourseRepositoryTest { private JdbcTemplate jdbcTemplate; private CourseRepository courseRepository; + private SessionRepository sessionRepository; @BeforeEach void setUp() { - courseRepository = new JdbcCourseRepository(jdbcTemplate); + sessionRepository = new JdbcSessionRepository(jdbcTemplate); + courseRepository = new JdbcCourseRepository(jdbcTemplate, sessionRepository); } @Test diff --git a/src/test/java/nextstep/courses/infrastructure/EnrollmentRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/EnrollmentRepositoryTest.java new file mode 100644 index 0000000000..805651282e --- /dev/null +++ b/src/test/java/nextstep/courses/infrastructure/EnrollmentRepositoryTest.java @@ -0,0 +1,44 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.enrollment.Enrollment; +import nextstep.courses.repository.EnrollmentRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.jdbc.core.JdbcTemplate; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +@JdbcTest +public class EnrollmentRepositoryTest { + private static final Logger LOGGER = LoggerFactory.getLogger(EnrollmentRepository.class); + + @Autowired + private JdbcTemplate jdbcTemplate; + + private EnrollmentRepository enrollmentRepository; + + @BeforeEach + void setUp() { + enrollmentRepository = new JdbcEnrollmentRepository(jdbcTemplate); + } + + @Test + void crud() { + Enrollment enrollment = new Enrollment(1L, 100L, LocalDateTime.now()); + enrollmentRepository.save(enrollment); + + List result = enrollmentRepository.findBySessionId(1L); + assertThat(result).hasSize(1); + + Enrollment saved = result.get(0); + assertThat(saved.getSessionId()).isEqualTo(1L); + assertThat(saved.getUserId()).isEqualTo(100L); + } +} diff --git a/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java new file mode 100644 index 0000000000..a3fe988aab --- /dev/null +++ b/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java @@ -0,0 +1,70 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.*; +import nextstep.courses.domain.enrollment.Money; +import nextstep.courses.domain.session.Session; +import nextstep.courses.repository.SessionRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.jdbc.core.JdbcTemplate; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@JdbcTest +public class SessionRepositoryTest { + private static final Logger LOGGER = LoggerFactory.getLogger(SessionRepository.class); + + @Autowired + private JdbcTemplate jdbcTemplate; + + private SessionRepository sessionRepository; + + @BeforeEach + void setUp() { + sessionRepository = new JdbcSessionRepository(jdbcTemplate); + } + + @Test + void crud() { + Session session = SessionTestBuilder.aSession() + .withCapacity(10) + .withPaidEnrollment(new Money(5000L)) + .build(); + int count = sessionRepository.save(session); + assertThat(count).isEqualTo(1); + Session savedSession = sessionRepository.findById(1L); + assertThat(session.getCoverImageName()).isEqualTo(savedSession.getCoverImageName()); + LOGGER.debug("Session: {}", savedSession); + } + + @Test + void findByCourseId() { + Session session = SessionTestBuilder.aSession() + .withId(1L) + .withCourseId(10L) + .withCapacity(10) + .withPaidEnrollment(new Money(5000L)) + .build(); + int count = sessionRepository.save(session); + + session = SessionTestBuilder.aSession() + .withId(2L) + .withCourseId(10L) + .withCapacity(10) + .withPaidEnrollment(new Money(5000L)) + .build(); + count += sessionRepository.save(session); + + assertThat(count).isEqualTo(2); + + List sessions = sessionRepository.findByCourseId(10L); + assertThat(sessions).hasSize(2); + LOGGER.debug("Sessions: {}", sessions); + } +}