diff --git a/src/main/java/nextstep/image/domain/Image.java b/src/main/java/nextstep/image/domain/Image.java index cc75f4be4..5dc7023e0 100644 --- a/src/main/java/nextstep/image/domain/Image.java +++ b/src/main/java/nextstep/image/domain/Image.java @@ -1,13 +1,43 @@ package nextstep.image.domain; public class Image { + private Long id; + private Long sessionId; + private String url; private ImageType type; private ImageShape shape; private Integer size; - public Image(final ImageType type, final ImageShape shape, final Integer size) { + public Image(final long id, final long sessionId, final String url, final ImageType type, final ImageShape shape, final Integer size) { + this.id = id; + this.sessionId = sessionId; + this.url = url; this.type = type; this.shape = shape; this.size = size; } + + public Long getSessionId() { + return sessionId; + } + + public String getUrl() { + return url; + } + + public String getType() { + return type.name(); + } + + public int getImageWidth() { + return shape.getWidth(); + } + + public int getImageHeight() { + return shape.getHeight(); + } + + public int getSize() { + return size; + } } diff --git a/src/main/java/nextstep/image/domain/ImageRepository.java b/src/main/java/nextstep/image/domain/ImageRepository.java new file mode 100644 index 000000000..bd15c1dd1 --- /dev/null +++ b/src/main/java/nextstep/image/domain/ImageRepository.java @@ -0,0 +1,6 @@ +package nextstep.image.domain; + +public interface ImageRepository { + int save(final Image image); + Image findById(final Long id); +} diff --git a/src/main/java/nextstep/image/domain/ImageShape.java b/src/main/java/nextstep/image/domain/ImageShape.java index 213dbde60..316e817dd 100644 --- a/src/main/java/nextstep/image/domain/ImageShape.java +++ b/src/main/java/nextstep/image/domain/ImageShape.java @@ -4,6 +4,10 @@ public class ImageShape { private ImageWidth width; private ImageHeight height; + public ImageShape(final int width, final int height) { + this(new ImageWidth(width), new ImageHeight(height)); + } + public ImageShape(ImageWidth width, ImageHeight height) { validateShape(width, height); this.width = width; @@ -15,4 +19,12 @@ private static void validateShape(final ImageWidth width, final ImageHeight heig throw new IllegalArgumentException("가로 세로 비율은 3:2이어야 합니다."); } } + + public int getWidth() { + return width.getWidth(); + } + + public int getHeight() { + return height.getHeight(); + } } diff --git a/src/main/java/nextstep/image/infrastructure/JdbcImageRepository.java b/src/main/java/nextstep/image/infrastructure/JdbcImageRepository.java new file mode 100644 index 000000000..c364a5251 --- /dev/null +++ b/src/main/java/nextstep/image/infrastructure/JdbcImageRepository.java @@ -0,0 +1,43 @@ +package nextstep.image.infrastructure; + +import nextstep.courses.domain.Course; +import nextstep.image.domain.*; +import org.springframework.jdbc.core.JdbcOperations; +import org.springframework.jdbc.core.RowMapper; + +import java.sql.Timestamp; +import java.time.LocalDateTime; + +public class JdbcImageRepository implements ImageRepository { + private JdbcOperations jdbcTemplate; + + public JdbcImageRepository(JdbcOperations jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Override + public int save(Image image) { + String sql = "insert into image (session_id, url, image_type, image_height, image_width, size) values(?, ?, ?,?, ?, ?)"; + return jdbcTemplate.update(sql, image.getSessionId(), image.getUrl(), image.getType(), image.getImageHeight(), image.getImageWidth(), image.getSize()); + } + + @Override + public Image findById(Long id) { + String sql = "select id, session_id, url, image_type, image_width, image_height, size from image where id = ?"; + RowMapper rowMapper = (rs, rowNum) -> new Image( + rs.getLong(1), + rs.getLong(2), + rs.getString(3), + ImageType.valueOf(rs.getString(4)), + new ImageShape(rs.getInt(5), rs.getInt(6)), + rs.getInt(7)); + return jdbcTemplate.queryForObject(sql, rowMapper, id); + } + + private LocalDateTime toLocalDateTime(Timestamp timestamp) { + if (timestamp == null) { + return null; + } + return timestamp.toLocalDateTime(); + } +} diff --git a/src/main/java/nextstep/sessions/domain/Charge.java b/src/main/java/nextstep/sessions/domain/Charge.java index e164c8b88..b2ec798fc 100644 --- a/src/main/java/nextstep/sessions/domain/Charge.java +++ b/src/main/java/nextstep/sessions/domain/Charge.java @@ -14,4 +14,12 @@ public Charge(ChargeStatus status, int price) { this.status = status; this.price = price; } + + public ChargeStatus getStatus() { + return status; + } + + public int getPrice() { + return price; + } } diff --git a/src/main/java/nextstep/sessions/domain/Enrollment.java b/src/main/java/nextstep/sessions/domain/Enrollment.java index 1d788b776..0c0c564f5 100644 --- a/src/main/java/nextstep/sessions/domain/Enrollment.java +++ b/src/main/java/nextstep/sessions/domain/Enrollment.java @@ -41,4 +41,15 @@ private boolean isFullCapacity() { return students.size() == capacity; } + public SessionStatus getSessionStatus() { + return sessionStatus; + } + + public int getCapacity() { + return capacity; + } + + public List getStudents() { + return students; + } } diff --git a/src/main/java/nextstep/sessions/domain/Session.java b/src/main/java/nextstep/sessions/domain/Session.java index ea1891a05..680e69d6c 100644 --- a/src/main/java/nextstep/sessions/domain/Session.java +++ b/src/main/java/nextstep/sessions/domain/Session.java @@ -4,25 +4,59 @@ public class Session { - private final SessionInfo sessionInfo; - private final Charge charge; + private final Long id; + private final SessionInfo sessionInfo; + private final Charge charge; private final Enrollment enrollment; - private final SessionDate sessionDate; + private final SessionDate sessionDate; - public Session(String title, Long creatorId, - ChargeStatus chargeStatus, int price, - int capacity, SessionStatus sessionStatus, - LocalDate startDate, LocalDate endDate) { - this(new SessionInfo(title, creatorId, null), + public Session(final Long id, String title, Long creatorId, + ChargeStatus chargeStatus, int price, + int capacity, SessionStatus sessionStatus, + LocalDate startDate, LocalDate endDate) { + this(id, new SessionInfo(title, creatorId, null), new Charge(chargeStatus, price), new Enrollment(sessionStatus, capacity), new SessionDate(new StartedAt(startDate), new EndedAt(endDate))); } - public Session(SessionInfo sessionInfo, Charge charge, Enrollment enrollment, SessionDate sessionDate) { + public Session(final Long id, SessionInfo sessionInfo, Charge charge, Enrollment enrollment, SessionDate sessionDate) { + this.id = id; this.sessionInfo = sessionInfo; this.charge = charge; this.enrollment = enrollment; this.sessionDate = sessionDate; } + + public String getTitle() { + return sessionInfo.getTitle(); + } + + public Long getCreatorId() { + return sessionInfo.getCreatorId(); + } + + public String getChargeStatus() { + return charge.getStatus().name(); + } + + public int getPrice() { + return charge.getPrice(); + } + + public LocalDate getStartedAt() { + return sessionDate.getStartedAt(); + } + + public LocalDate getEndedAt() { + return sessionDate.getEndedAt(); + } + + public String getSessionStatus() { + return enrollment.getSessionStatus().name(); + } + + public int getCapacity() { + return enrollment.getCapacity(); + } } diff --git a/src/main/java/nextstep/sessions/domain/SessionDate.java b/src/main/java/nextstep/sessions/domain/SessionDate.java index 124d8504e..aa8ce910f 100644 --- a/src/main/java/nextstep/sessions/domain/SessionDate.java +++ b/src/main/java/nextstep/sessions/domain/SessionDate.java @@ -18,4 +18,12 @@ private static void validateSessionDate(final LocalDate startedAt, final LocalDa throw new IllegalArgumentException("강의 종료일보다 강의 시작일이 늦을 수 없습니다."); } } + + public LocalDate getStartedAt() { + return startedAt.getStartedAt(); + } + + public LocalDate getEndedAt() { + return endedAt.getEndedAt(); + } } diff --git a/src/main/java/nextstep/sessions/domain/SessionInfo.java b/src/main/java/nextstep/sessions/domain/SessionInfo.java index b649413c1..5a0eecc62 100644 --- a/src/main/java/nextstep/sessions/domain/SessionInfo.java +++ b/src/main/java/nextstep/sessions/domain/SessionInfo.java @@ -13,4 +13,16 @@ public SessionInfo(String title, Long creatorId, Image image) { this.creatorId = creatorId; this.image = image; } + + public String getTitle() { + return title; + } + + public Long getCreatorId() { + return creatorId; + } + + public Image getImage() { + return image; + } } diff --git a/src/main/java/nextstep/sessions/domain/SessionRepository.java b/src/main/java/nextstep/sessions/domain/SessionRepository.java new file mode 100644 index 000000000..d7a04115c --- /dev/null +++ b/src/main/java/nextstep/sessions/domain/SessionRepository.java @@ -0,0 +1,6 @@ +package nextstep.sessions.domain; + +public interface SessionRepository { + int save(final Session session); + Session findById(Long id); +} diff --git a/src/main/java/nextstep/sessions/infrastructure/JdbcSessionRepository.java b/src/main/java/nextstep/sessions/infrastructure/JdbcSessionRepository.java new file mode 100644 index 000000000..6c6a4af48 --- /dev/null +++ b/src/main/java/nextstep/sessions/infrastructure/JdbcSessionRepository.java @@ -0,0 +1,49 @@ +package nextstep.sessions.infrastructure; + +import nextstep.sessions.domain.*; +import org.springframework.jdbc.core.JdbcOperations; +import org.springframework.jdbc.core.RowMapper; + +import java.sql.Timestamp; +import java.time.LocalDate; + +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 (title, creator_id, charge_status, price, session_status, capacity, started_at, ended_at) values(?, ?, ?,?, ?, ?, ?, ?)"; + return jdbcTemplate.update(sql, session.getTitle(), session.getCreatorId(),session.getChargeStatus(), session.getPrice(), session.getSessionStatus(), session.getCapacity(), session.getStartedAt(), session.getEndedAt()); + } + + @Override + public Session findById(Long id) { + String sql = "select session.id, session.title, session.creator_id, session.charge_status, session.price, session.capacity, session.session_status,session.started_at, session.ended_at " + + "from session where id = ?"; + + RowMapper rowMapper = (rs, rowNum) -> new Session( + rs.getLong(1), + rs.getString(2), + rs.getLong(3), + ChargeStatus.valueOf(rs.getString(4)), + rs.getInt(5), + rs.getInt(6), + SessionStatus.valueOf(rs.getString(7)), + toLocalDate(rs.getTimestamp(8)), + toLocalDate(rs.getTimestamp(9)) + ); + + return jdbcTemplate.queryForObject(sql, rowMapper, id); + } + + private LocalDate toLocalDate(Timestamp timestamp) { + if (timestamp == null) { + return null; + } + return timestamp.toLocalDateTime().toLocalDate(); + } +} diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 8d5a988c8..8941e1509 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -48,3 +48,27 @@ create table delete_history ( deleted_by_id bigint, primary key (id) ); + +create table image ( + id bigint generated by default as identity, + session_id bigint not null, + url varchar(255), + image_type varchar(255), + image_width bigint, + image_height bigint, + size bigint, + primary key (id) +); + +create table session ( + id bigint generated by default as identity, + title varchar(255) not null, + creator_id bigint not null, + charge_status varchar(255) not null, + price bigint not null, + session_status varchar(255) not null, + capacity bigint not null, + started_at timestamp, + ended_at timestamp, + primary key (id) +); diff --git a/src/test/java/fixture/sessions/domain/SessionFixture.java b/src/test/java/fixture/sessions/domain/SessionFixture.java index 6ea3dfb0a..f687dc06f 100644 --- a/src/test/java/fixture/sessions/domain/SessionFixture.java +++ b/src/test/java/fixture/sessions/domain/SessionFixture.java @@ -8,14 +8,14 @@ public class SessionFixture { public static Session createSessionWithEnrollment(int capacity, SessionStatus status) { - return new Session( + return new Session(1L, "TDD, 클린 코드 with Java 16기", 2L, ChargeStatus.PAID, 800_000, capacity, status, LocalDate.now(), LocalDate.now().plusDays(30)); } public static Session createSessionWithSessionDate(LocalDate startDate, LocalDate endDate) { - return new Session( + return new Session(1L, "TDD, 클린 코드 with Java 16기", 2L, ChargeStatus.PAID, 800_000, 10, SessionStatus.OPENED, diff --git a/src/test/java/nextstep/image/domain/ImageTest.java b/src/test/java/nextstep/image/domain/ImageTest.java new file mode 100644 index 000000000..8c2986035 --- /dev/null +++ b/src/test/java/nextstep/image/domain/ImageTest.java @@ -0,0 +1,48 @@ +package nextstep.image.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; + +public class ImageTest { + @DisplayName("이미지 크기는 1MB 이하여야 한다.") + @Test + public void imageSizeLessThan1MB() { + //given & when & then + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> { + new ImageSize(1000000); + }).withMessageMatching("이미지 크기는 1메가 바이트를 넘을 수 없습니다."); + } + + @DisplayName("이미지의 width는 300픽셀 이상이어야 한다.") + @Test + public void imageWidthOver300Pixel() { + //given + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> { + new ImageWidth(200); + }).withMessageMatching("이미지 너비는 300px보다 커야 합니다."); + } + + @DisplayName("이미지의 height는 200픽셀 이상이어야 한다.") + @Test + public void imageHeightOver200Pixel() { + //given + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> { + new ImageHeight(100); + }).withMessageMatching("이미지 높이는 200px보다 커야 합니다."); + } + + @DisplayName("이미지의 width와 height의 비율은 3:2여야 한다.") + @Test + public void widthAndHeightRatioEqual3To2() { + //given + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> { + new ImageShape(new ImageWidth(400), new ImageHeight(400)); + }).withMessageMatching("가로 세로 비율은 3:2이어야 합니다."); + } +} diff --git a/src/test/java/nextstep/image/infrastructure/ImageRepositoryTest.java b/src/test/java/nextstep/image/infrastructure/ImageRepositoryTest.java new file mode 100644 index 000000000..31a1e06e1 --- /dev/null +++ b/src/test/java/nextstep/image/infrastructure/ImageRepositoryTest.java @@ -0,0 +1,40 @@ +package nextstep.image.infrastructure; + +import nextstep.image.domain.Image; +import nextstep.image.domain.ImageRepository; +import nextstep.image.domain.ImageShape; +import nextstep.image.domain.ImageType; +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 static org.assertj.core.api.Assertions.assertThat; + +@JdbcTest +public class ImageRepositoryTest { + private static final Logger LOGGER = LoggerFactory.getLogger(ImageRepositoryTest.class); + + @Autowired + private JdbcTemplate jdbcTemplate; + + private ImageRepository imageRepository; + + @BeforeEach + void setUp() { + imageRepository = new JdbcImageRepository(jdbcTemplate); + } + + @Test + void crud() { + Image image = new Image(1L, 1L, "image1.png", ImageType.GIF, new ImageShape(1200, 800), 100); + int count = imageRepository.save(image); + assertThat(count).isEqualTo(1); + Image savedImage = imageRepository.findById(1L); + assertThat(savedImage.getUrl()).isEqualTo(image.getUrl()); + LOGGER.debug("Image: {}", savedImage); + } +} diff --git a/src/test/java/nextstep/sessions/domain/SessionTest.java b/src/test/java/nextstep/sessions/domain/SessionTest.java index e28695656..e113af3b6 100644 --- a/src/test/java/nextstep/sessions/domain/SessionTest.java +++ b/src/test/java/nextstep/sessions/domain/SessionTest.java @@ -5,8 +5,7 @@ import java.time.LocalDate; -import static fixture.sessions.domain.SessionFixture.*; -import static org.assertj.core.api.Assertions.assertThat; +import static fixture.sessions.domain.SessionFixture.createSessionWithSessionDate; import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; public class SessionTest { diff --git a/src/test/java/nextstep/sessions/infrastructure/SessionRepositoryTest.java b/src/test/java/nextstep/sessions/infrastructure/SessionRepositoryTest.java new file mode 100644 index 000000000..265dbf867 --- /dev/null +++ b/src/test/java/nextstep/sessions/infrastructure/SessionRepositoryTest.java @@ -0,0 +1,41 @@ +package nextstep.sessions.infrastructure; + +import fixture.sessions.domain.SessionFixture; +import nextstep.courses.infrastructure.CourseRepositoryTest; +import nextstep.sessions.domain.Session; +import nextstep.sessions.domain.SessionRepository; +import nextstep.sessions.domain.SessionStatus; +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 static org.assertj.core.api.Assertions.assertThat; + +@JdbcTest +public class SessionRepositoryTest { + private static final Logger LOGGER = LoggerFactory.getLogger(CourseRepositoryTest.class); + + @Autowired + private JdbcTemplate jdbcTemplate; + + private SessionRepository sessionRepository; + + @BeforeEach + void setUp() { + sessionRepository = new JdbcSessionRepository(jdbcTemplate); + } + + @Test + void crud() { + Session session = SessionFixture.createSessionWithEnrollment(50, SessionStatus.OPENED); + int count = sessionRepository.save(session); + assertThat(count).isEqualTo(1); + Session savedSession = sessionRepository.findById(1L); + assertThat(savedSession.getTitle()).isEqualTo(session.getTitle()); + LOGGER.debug("Course: {}", savedSession); + } +}