diff --git a/src/main/java/nextstep/courses/domain/CourseRepository.java b/src/main/java/nextstep/courses/domain/CourseRepository.java index 6aaeb638d..02320f837 100644 --- a/src/main/java/nextstep/courses/domain/CourseRepository.java +++ b/src/main/java/nextstep/courses/domain/CourseRepository.java @@ -2,6 +2,5 @@ public interface CourseRepository { int save(Course course); - Course findById(Long id); } diff --git a/src/main/java/nextstep/courses/domain/Image.java b/src/main/java/nextstep/courses/domain/Image.java deleted file mode 100644 index ea7c7fbc0..000000000 --- a/src/main/java/nextstep/courses/domain/Image.java +++ /dev/null @@ -1,62 +0,0 @@ -package nextstep.courses.domain; - -import java.util.Objects; - -public class Image { - private final int size; - private final int MB = 1024 * 1024; - private final Type type; - private final int width; - private final int height; - - public Image(int size, Type type, int width, int height) { - imagePixel(width, height); - imageSize(size); - imageType(type); - this.size = size; - this.type = type; - this.width = width; - this.height = height; - } - - private void imageSize(int size) { - if (size > MB) - throw new IllegalArgumentException("이미지 파일 크기가 너무 큽니다"); - } - private void imagePixel(int width, int height) { - if (width < 300 || height < 200) - throw new IllegalArgumentException("크기가 맞지 않습니다"); - if (!(width * 2 == height * 3)) - throw new IllegalArgumentException("비율이 맞지 않습니다"); - } - - private void imageType(Type type) { - if (type == null) - throw new IllegalArgumentException("잘못된 파일 타입입니다"); - } - - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Image image = (Image) o; - return size == image.size && width == image.width && height == image.height && type == image.type; - } - - @Override - public int hashCode() { - return Objects.hash(size, MB, type, width, height); - } - - @Override - public String toString() { - return "Image{" + - "size=" + size + - ", MB=" + MB + - ", type=" + type + - ", width=" + width + - ", height=" + height + - '}'; - } -} diff --git a/src/main/java/nextstep/courses/domain/Session.java b/src/main/java/nextstep/courses/domain/Session.java index 85d10c051..424414664 100644 --- a/src/main/java/nextstep/courses/domain/Session.java +++ b/src/main/java/nextstep/courses/domain/Session.java @@ -1,5 +1,6 @@ package nextstep.courses.domain; +import nextstep.image.domain.Image; import nextstep.payments.domain.Payment; import java.time.LocalDateTime; diff --git a/src/main/java/nextstep/courses/domain/SessionStatus.java b/src/main/java/nextstep/courses/domain/SessionStatus.java index 8921f0436..611c4c293 100644 --- a/src/main/java/nextstep/courses/domain/SessionStatus.java +++ b/src/main/java/nextstep/courses/domain/SessionStatus.java @@ -2,22 +2,12 @@ public class SessionStatus { - private String status; - private final String prepare = "prepare"; - private final String end = "end"; - private final String recruit = "recruit"; - + private Status status; public SessionStatus(String status) { - statusCheck(status); - this.status = status; - } - - private void statusCheck(String status) { - if (!(status.equals(prepare) || status.equals(end) || status.equals(recruit))) - throw new IllegalArgumentException("잘못된 강의 상태 입니다"); + this.status = Status.fromString(status); } public boolean check() { - return status.equals(recruit); + return this.status == Status.RECRUIT; } } diff --git a/src/main/java/nextstep/courses/domain/Status.java b/src/main/java/nextstep/courses/domain/Status.java new file mode 100644 index 000000000..69eae86f8 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/Status.java @@ -0,0 +1,25 @@ +package nextstep.courses.domain; + +public enum Status { + + PREPARE("prepare"), + END("end"), + RECRUIT("recruit"); + + private final String status; + Status(String status) { + this.status = status; + } + + public String getStatus() { + return status; + } + + public static Status fromString(String status) { + for (Status state : Status.values()) { + if (state.getStatus().equals(status)) + return state; + } + throw new IllegalArgumentException(status + "는 잘못된 강의 상태 입니다"); + } +} diff --git a/src/main/java/nextstep/courses/domain/Type.java b/src/main/java/nextstep/courses/domain/Type.java deleted file mode 100644 index 1d9ed8eea..000000000 --- a/src/main/java/nextstep/courses/domain/Type.java +++ /dev/null @@ -1,5 +0,0 @@ -package nextstep.courses.domain; - -public enum Type { - git, jpg, jpeg, png, svg; -} diff --git a/src/main/java/nextstep/image/domain/Image.java b/src/main/java/nextstep/image/domain/Image.java new file mode 100644 index 000000000..470b855a5 --- /dev/null +++ b/src/main/java/nextstep/image/domain/Image.java @@ -0,0 +1,138 @@ +package nextstep.image.domain; + +import java.util.Objects; + +public class Image { + + private Long id; + private Long sessionId; + private long size; + private final int MB = 1024 * 1024; + private final int MINIMUM_WIDTH = 300; + private final int MINIMUM_HEIGHT = 200; + private ImageType imageType; + private int width; + private int height; + private String filePath; + private String fileName; + private String extension; + + + public Image(int size, ImageType type, int width, int height) { + + this.size = size; + this.imageType = type; + this.width = width; + this.height = height; + } + + public Image(Long sessionId, int width, int height, long size, String fileName, String extension, String filePath) { + this(0L, sessionId, width, height, size, fileName, extension, filePath); + } + + public Image(Long id, Long sessionId, int width, int height, long size, String fileName, String extension, String filePath) { + if (sessionId == null) + throw new IllegalArgumentException("강의 번호가 잘못되었습니다"); + imagePixel(width, height); + imageSize(size); + imageType(fileName); + this.id = id; + this.sessionId = sessionId; + this.width = width; + this.height = height; + this.size = size; + this.fileName = fileName; + this.filePath = filePath; + this.extension = extension; + } + + private void imageSize(long size) { + if (size > 1 * MB) + throw new IllegalArgumentException("이미지 파일 크기가 너무 큽니다"); + } + private void imagePixel(int width, int height) { + if (width < MINIMUM_WIDTH || height < MINIMUM_HEIGHT) + throw new IllegalArgumentException("크기가 맞지 않습니다"); + if (!(width * 2 == height * 3)) + throw new IllegalArgumentException("비율이 맞지 않습니다"); + } + + private void imageType(String type) { + for (ImageType value : ImageType.values()) { + if (!value.name().equals(type)) + throw new IllegalArgumentException("잘못된 파일 타입입니다"); + } + } + + public long getSize() { + return size; + } + + public ImageType getImageType() { + return imageType; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public int getMB() { + return MB; + } + + public int getMINIMUM_WIDTH() { + return MINIMUM_WIDTH; + } + + public int getMINIMUM_HEIGHT() { + return MINIMUM_HEIGHT; + } + + public Long getId() { + return id; + } + + public Long getSessionId() { + return sessionId; + } + + public String getFilePath() { + return filePath; + } + + public String getFileName() { + return fileName; + } + + public String getExtension() { + return extension; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Image image = (Image) o; + return size == image.size && width == image.width && height == image.height && imageType == image.imageType; + } + + @Override + public int hashCode() { + return Objects.hash(size, MB, imageType, width, height); + } + + @Override + public String toString() { + return "Image{" + + "size=" + size + + ", MB=" + MB + + ", type=" + imageType + + ", width=" + width + + ", height=" + height + + '}'; + } +} 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..3359b03c5 --- /dev/null +++ b/src/main/java/nextstep/image/domain/ImageRepository.java @@ -0,0 +1,16 @@ +package nextstep.image.domain; + +import nextstep.image.domain.Image; + +import java.util.List; + +public interface ImageRepository { + + int save(Image image); + + Image findById(Long id); + + List findBySessionId(Long id); + + void saveAll(List images); +} diff --git a/src/main/java/nextstep/image/domain/ImageType.java b/src/main/java/nextstep/image/domain/ImageType.java new file mode 100644 index 000000000..b38aee682 --- /dev/null +++ b/src/main/java/nextstep/image/domain/ImageType.java @@ -0,0 +1,16 @@ +package nextstep.image.domain; + +public enum ImageType { + + GIT("git"), + JPG("jpg"), + JPEG("jpeg"), + PNG("png"), + SVG("svg"); + + String type; + + ImageType(String type) { + this.type = type; + } +} 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..70fb86cdf --- /dev/null +++ b/src/main/java/nextstep/image/infrastructure/JdbcImageRepository.java @@ -0,0 +1,84 @@ +package nextstep.image.infrastructure; + +import nextstep.image.domain.Image; +import nextstep.image.domain.ImageRepository; +import org.springframework.jdbc.core.JdbcOperations; +import org.springframework.jdbc.core.ParameterizedPreparedStatementSetter; +import org.springframework.jdbc.core.PreparedStatementSetter; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository("ImageRepository") + +public class JdbcImageRepository implements ImageRepository { + + private final JdbcOperations jdbcTempleate; + + public JdbcImageRepository(JdbcOperations jdbcTempleate) { + this.jdbcTempleate = jdbcTempleate; + } + + @Override + public int save(Image image) { + String sql = "INSERT INTO nextstep.image(session_id, width, height, size, file_name, extension, file_path)\n" + "VALUES (?, ?, ?, ?, ?, ?, ?)"; + + return jdbcTempleate.update(sql, values(image)); + } + + private PreparedStatementSetter values(Image image) { + return ps -> { + ps.setLong(1, image.getSessionId()); + ps.setInt(2, image.getWidth()); + ps.setInt(3, image.getHeight()); + ps.setLong(4, image.getSize()); + ps.setString(5, image.getFileName()); + ps.setString(6, image.getExtension()); + ps.setString(7, image.getFilePath()); + }; + } + + @Override + public Image findById(Long id) { + String sql = "SELECT id, session_id, width, height, size, file_name, extension, file_path from image where id = ?"; + return jdbcTempleate.queryForObject(sql, imageRowMapper(), id); + } + + private RowMapper imageRowMapper() { + return (rs, rowNum) -> new Image( + rs.getLong(1), + rs.getLong(2), + rs.getInt(3), + rs.getInt(4), + rs.getLong(5), + rs.getString(6), + rs.getString(7), + rs.getString(8) + ); + } + + @Override + public List findBySessionId(Long sessionId) { + String sql = "SELECT id, session_id, width, height, size, file_name, extension, file_path from nextstep.image where id = ?"; + return jdbcTempleate.query(sql, imageRowMapper(), sessionId); + } + + @Override + public void saveAll(List images) { + String sql = "INSERT INTO nextstep.image(session_id, width, height, size, file_name, extension, file_path)\n" + "VALUES (?, ?, ?, ?, ?, ?, ?)"; + jdbcTempleate.batchUpdate(sql, images, images.size(), values()); + } + + private ParameterizedPreparedStatementSetter values() { + return (ps, image) -> { + ps.setLong(1, image.getSessionId()); + ps.setInt(2, image.getWidth()); + ps.setInt(3, image.getHeight()); + ps.setLong(4, image.getSize()); + ps.setString(5, image.getFileName()); + ps.setString(6, image.getExtension()); + ps.setString(7, image.getFilePath()); + }; + } +} diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 8d5a988c8..8fcb675e5 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -1,5 +1,5 @@ create table course ( - id bigint generated by default as identity, + id bigint AUTO_INCREMENT, title varchar(255) not null, creator_id bigint not null, created_at timestamp not null, @@ -8,7 +8,7 @@ create table course ( ); create table ns_user ( - id bigint generated by default as identity, + id bigint AUTO_INCREMENT, user_id varchar(20) not null, password varchar(20) not null, name varchar(20) not null, @@ -19,10 +19,10 @@ create table ns_user ( ); create table question ( - id bigint generated by default as identity, + id bigint AUTO_INCREMENT, created_at timestamp not null, updated_at timestamp, - contents clob, + contents text, deleted boolean not null, title varchar(100) not null, writer_id bigint, @@ -30,10 +30,10 @@ create table question ( ); create table answer ( - id bigint generated by default as identity, + id bigint AUTO_INCREMENT, created_at timestamp not null, updated_at timestamp, - contents clob, + contents text, deleted boolean not null, question_id bigint, writer_id bigint, @@ -48,3 +48,63 @@ create table delete_history ( deleted_by_id bigint, primary key (id) ); + +CREATE TABLE session ( + session_id BIGINT AUTO_INCREMENT PRIMARY KEY, + title VARCHAR(255) NOT NULL, + start_date TIMESTAMP NOT NULL, + end_date TIMESTAMP NOT NULL, + image_url VARCHAR(255), + cost DECIMAL(10, 2) NOT NULL, + status VARCHAR(50) NOT NULL, + limit_students INT NOT NULL, + foreign key (course_id) references course(id) +); + +create table image ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + session_id BiGINT NOT NULL, + width INT NOT NULL, + height INT NOT NULL, + size BIGINT NOT NULL, + file_name VARCHAR(100) NOT NULL, + extension VARCHAR(100) NOT NULL, + file_path VARCHAR(100) NOT NULL +); + +INSERT INTO ns_user (id, user_id, password, name, email, created_at) +values (1, 'javajigi', 'test', '자바지기', 'javajigi@slipp.net', CURRENT_TIMESTAMP()); +INSERT INTO ns_user (id, user_id, password, name, email, created_at) +values (2, 'sanjigi', 'test', '산지기', 'sanjigi@slipp.net', CURRENT_TIMESTAMP()); + +INSERT INTO question (id, writer_id, title, contents, created_at, deleted) +VALUES (1, 1, '국내에서 Ruby on Rails와 Play가 활성화되기 힘든 이유는 뭘까?', + 'Ruby on Rails(이하 RoR)는 2006년 즈음에 정말 뜨겁게 달아올랐다가 금방 가라 앉았다. Play 프레임워크는 정말 한 순간 잠시 눈에 뜨이다가 사라져 버렸다. RoR과 Play 기반으로 개발을 해보면 정말 생산성이 높으며, 웹 프로그래밍이 재미있기까지 하다. Spring MVC + JPA(Hibernate) 기반으로 진행하면 설정할 부분도 많고, 기본으로 지원하지 않는 기능도 많아 RoR과 Play에서 기본적으로 지원하는 기능을 서비스하려면 추가적인 개발이 필요하다.', + CURRENT_TIMESTAMP(), false); + +INSERT INTO question (id, writer_id, title, contents, created_at, deleted) +VALUES (2, 2, 'runtime 에 reflect 발동 주체 객체가 뭔지 알 방법이 있을까요?', + '설계를 희한하게 하는 바람에 꼬인 문제같긴 합니다만. 여쭙습니다. 상황은 mybatis select 실행될 시에 return object 의 getter 가 호출되면서인데요. getter 안에 다른 property 에 의존중인 코드가 삽입되어 있어서, 만약 다른 mybatis select 구문에 해당 property 가 없다면 exception 이 발생하게 됩니다.', + CURRENT_TIMESTAMP(), false); + +INSERT INTO answer (writer_id, contents, created_at, question_id, deleted) +VALUES (1, + 'http://underscorejs.org/docs/underscore.html Underscore.js 강추합니다! 쓸일도 많고, 코드도 길지 않고, 자바스크립트의 언어나 기본 API를 보완하는 기능들이라 자바스크립트 이해에 도움이 됩니다. 무엇보다 라이브러리 자체가 아주 유용합니다.', + CURRENT_TIMESTAMP(), 1, false); + +INSERT INTO answer (writer_id, contents, created_at, question_id, deleted) +VALUES (2, '언더스코어 강력 추천드려요. 다만 최신 버전을 공부하는 것보다는 0.10.0 버전부터 보는게 더 좋더군요. 코드의 변천사도 알 수 있고, 최적화되지 않은 코드들이 기능은 그대로 두고 최적화되어 가는 걸 보면 재미가 있습니다 :)', + CURRENT_TIMESTAMP(), 1, false); + +INSERT INTO image(session_id, width, height, size, file_name, extension, file_path) +VALUES (1, 1200, 800, 340797, 'playground.jpeg', 'jpeg', 'playground.jpeg'); +INSERT INTO image(session_id, width, height, size, file_name, extension, file_path) +VALUES (1, 300, 200, 999, 'example.png', 'png', 'example.png'); + + +create database nextstep; + +use nextstep; +show databases; + +show tables; diff --git a/src/test/java/nextstep/courses/domain/ImageTest.java b/src/test/java/nextstep/courses/domain/ImageTest.java index ebf2ac2f8..090f307c3 100644 --- a/src/test/java/nextstep/courses/domain/ImageTest.java +++ b/src/test/java/nextstep/courses/domain/ImageTest.java @@ -1,5 +1,7 @@ package nextstep.courses.domain; +import nextstep.image.domain.Image; +import nextstep.image.domain.ImageType; import org.junit.jupiter.api.Test; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; @@ -9,10 +11,10 @@ class ImageTest { @Test public void 이미지생성() { - Image image = new Image(1020, Type.git, 300, 200); + Image image = new Image(1020, ImageType.GIT, 300, 200); assertThatThrownBy(() -> { - new Image(100000000, Type.git, 300, 200); + new Image(100000000, ImageType.GIT, 300, 200); }).isInstanceOf(IllegalArgumentException.class).hasMessageContaining("이미지 파일 크기가 너무 큽니다"); assertThatThrownBy(() -> { @@ -20,9 +22,9 @@ class ImageTest { }).isInstanceOf(IllegalArgumentException.class).hasMessageContaining("잘못된 파일 타입입니다"); assertThatThrownBy(() -> { - new Image(1020, Type.git, 100, 200); + new Image(1020, ImageType.GIT, 100, 200); }).isInstanceOf(IllegalArgumentException.class).hasMessageContaining("크기가 맞지 않습니다"); - assertThat(image).isEqualTo(new Image(1020, Type.git, 300, 200)); + assertThat(image).isEqualTo(new Image(1020, ImageType.GIT, 300, 200)); } } \ No newline at end of file diff --git a/src/test/java/nextstep/courses/domain/SessionStatusTest.java b/src/test/java/nextstep/courses/domain/SessionStatusTest.java index 9760cf7ea..90f22b868 100644 --- a/src/test/java/nextstep/courses/domain/SessionStatusTest.java +++ b/src/test/java/nextstep/courses/domain/SessionStatusTest.java @@ -11,9 +11,10 @@ class SessionStatusTest { @Test void check() { SessionStatus status = new SessionStatus("recruit"); + String word = "Wrong"; assertThatThrownBy(() -> { - new SessionStatus("Wrong"); - }).isInstanceOf(IllegalArgumentException.class).hasMessageContaining("잘못된 강의 상태 입니다"); + new SessionStatus(word); + }).isInstanceOf(IllegalArgumentException.class).hasMessageContaining(word + "는 잘못된 강의 상태 입니다"); assertThat(status.check()).isEqualTo(true); }