diff --git a/STEP3README.md b/STEP3README.md new file mode 100644 index 0000000000..53d2378ac8 --- /dev/null +++ b/STEP3README.md @@ -0,0 +1,21 @@ +# 학습 관리 시스템(Learning Management System) + +## step3 요구사항 + +1. [x] 앞단계에서 구현한 도메인 모델 DB와 매핑하고 데이터 저장 +2. + 2. 테이블 설계하고 객체매핑 +2. [x] Payments는 테이블 매핑 미고려 + +## 관련 클래스 + +1. DB 테이블 resources/schema.sql +2. jdbcCourseRepository +3. CourseRepositoryTest + + +## 주요 피드백 +1. 숫자로 순서를 써서 하는건 실수 및 어려운 문제가 있음 +-> NamedParameterJdbcTemplate사용해보기 + +2. \ No newline at end of file diff --git a/src/main/java/nextstep/courses/Exception/ResponseType.java b/src/main/java/nextstep/courses/Exception/ResponseType.java deleted file mode 100644 index 36a62529df..0000000000 --- a/src/main/java/nextstep/courses/Exception/ResponseType.java +++ /dev/null @@ -1,20 +0,0 @@ -package nextstep.courses.Exception; - -public enum ResponseType { - NOT_ALLOWED_DATE_MESSAGE("허용되지 않은 시작날짜입니다."), - OVER_MAX_IMAGE_CAPACITY("이미지 사이즈는 1MB 이하여야 합니다."), - IMAGE_SIZE_ERROR("이미지 사이즈 오류입니다."), - IMAGE_PERCENT_ERROR("이미지 비율 오류"), - NOT_ALLOWED_PREMIUM_AMOUNT("유료강의 금액은 0원이 될 수 없습니다."), - NOT_ALLOWED_FREE_AMOUNT("무료 강의 금액이 0원이 아닙니다."), - NOT_MACHING_SESSION_AMOUNT("강의 금액과 맞지 않습니다."), - MAX_STUDENTS_OVER("수강인원 초과"), - INVALID_SESSION_STATE("강의신청 기간이 아닙니다."), - INVALID_IMAGE_TYPE("이미지 타입이 올바르지 않습니다."); - - public String message; - - ResponseType(String message) { - this.message = message; - } -} diff --git a/src/main/java/nextstep/courses/domain/Course.java b/src/main/java/nextstep/courses/domain/Course.java index 911ba1112b..badecc0325 100644 --- a/src/main/java/nextstep/courses/domain/Course.java +++ b/src/main/java/nextstep/courses/domain/Course.java @@ -14,23 +14,27 @@ public class Course { private Long creatorId; private LocalDateTime createdAt; private LocalDateTime updatedAt; - private List sessionList; + private List sessions; public Course() { } public Course(String title, Long creatorId) { - this(0L, title, creatorId, LocalDateTime.now(), null); + this(0L, title, creatorId, LocalDateTime.now(), null,1); } - public Course(Long id, String title, Long creatorId, LocalDateTime createdAt, LocalDateTime updatedAt) { + public Course(Long id, String title, Long creatorId, LocalDateTime createdAt, LocalDateTime updatedAt, int classNumber) { + this(id, title, creatorId, createdAt, updatedAt, classNumber, new ArrayList<>()); + } + + public Course(Long id, String title, Long creatorId, LocalDateTime createdAt, LocalDateTime updatedAt, int classNumber, List sessions) { this.id = id; this.title = title; this.creatorId = creatorId; this.createdAt = createdAt; this.updatedAt = updatedAt; - sessionList = new ArrayList<>(); - classNumber = count.incrementAndGet(); + this.sessions = sessions; + this.classNumber = classNumber; } public String getTitle() { @@ -45,6 +49,10 @@ public LocalDateTime getCreatedAt() { return createdAt; } + public int getClassNumber() { + return classNumber; + } + @Override public String toString() { return "Course{" + @@ -55,4 +63,6 @@ public String toString() { ", updatedAt=" + updatedAt + '}'; } + + } diff --git a/src/main/java/nextstep/courses/domain/CourseSessionRepository.java b/src/main/java/nextstep/courses/domain/CourseSessionRepository.java new file mode 100644 index 0000000000..9a80203891 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/CourseSessionRepository.java @@ -0,0 +1,8 @@ +package nextstep.courses.domain; + +import java.util.List; + +public interface CourseSessionRepository { + int save(Long courseId, List sessionIds); + List findByCourseId(Long id); +} diff --git a/src/main/java/nextstep/courses/domain/PricingType.java b/src/main/java/nextstep/courses/domain/PricingType.java index e0d5512c70..e1c27b804e 100644 --- a/src/main/java/nextstep/courses/domain/PricingType.java +++ b/src/main/java/nextstep/courses/domain/PricingType.java @@ -1,12 +1,14 @@ package nextstep.courses.domain; -import nextstep.courses.Exception.CustomException; +import nextstep.courses.exception.CustomException; import nextstep.payments.domain.Payment; public class PricingType { private final int sessionAmount; - private boolean isPremium; + private final boolean isPremium; + + public PricingType(boolean isPremium, int sessionAmount) { validate(isPremium,sessionAmount); @@ -32,4 +34,12 @@ public void validateAmount(Payment payment) { throw CustomException.NOT_MATCHING_SESSION_AMOUNT; } } + + public int getSessionAmount() { + return sessionAmount; + } + + public boolean getIsPremium() { + return isPremium; + } } diff --git a/src/main/java/nextstep/courses/domain/Session.java b/src/main/java/nextstep/courses/domain/Session.java index 0fdab2818e..6c3ca20ff2 100644 --- a/src/main/java/nextstep/courses/domain/Session.java +++ b/src/main/java/nextstep/courses/domain/Session.java @@ -1,30 +1,48 @@ package nextstep.courses.domain; -import nextstep.courses.Exception.CustomException; -import nextstep.courses.domain.sessionimage.SessionImage; +import nextstep.courses.exception.CustomException; +import nextstep.courses.domain.image.SessionImage; import nextstep.payments.domain.Payment; -import nextstep.users.domain.NsUser; import java.util.ArrayList; import java.util.List; public class Session { - private List students; + private Long id; + private List students; private PricingType pricingType; private SessionState state; private SessionImage image; private int maxStudentCount; private SessionDate date; + private Long courseId; + public Session(PricingType pricingType, int maxStudentCount, SessionState state, SessionDate date , SessionImage image) { + this(0L, new ArrayList(),pricingType,state,image,maxStudentCount,date,0L ); + } + + public Session(List students, PricingType pricingType, SessionState state, SessionImage image, int maxStudentCount, SessionDate date) { + this.id = 0L; + this.students = students; this.pricingType = pricingType; - this.students = new ArrayList<>(); - this.maxStudentCount = maxStudentCount; this.state = state; - this.date = date; this.image = image; + this.maxStudentCount = maxStudentCount; + this.date = date; + this.courseId = 0L; + } + public Session(Long id, List students, PricingType pricingType, SessionState state, SessionImage image, int maxStudentCount, SessionDate date, Long courseId) { + this.id = id; + this.students = students; + this.pricingType = pricingType; + this.state = state; + this.image = image; + this.maxStudentCount = maxStudentCount; + this.date = date; + this.courseId = courseId; } public void requestSession(Payment payment) { @@ -47,4 +65,28 @@ private void validateSessionState() { } + public List getStudents() { + return students; + } + + public PricingType getPricingType() { + return pricingType; + } + + public SessionState getState() { + return state; + } + + public SessionImage getImage() { + return image; + } + + public int getMaxStudentCount() { + return maxStudentCount; + } + + public SessionDate getDate() { + return date; + } + } diff --git a/src/main/java/nextstep/courses/domain/SessionDate.java b/src/main/java/nextstep/courses/domain/SessionDate.java index ec55634350..86ebe42fbe 100644 --- a/src/main/java/nextstep/courses/domain/SessionDate.java +++ b/src/main/java/nextstep/courses/domain/SessionDate.java @@ -1,6 +1,6 @@ package nextstep.courses.domain; -import nextstep.courses.Exception.CustomException; +import nextstep.courses.exception.CustomException; import java.time.LocalDateTime; @@ -15,4 +15,12 @@ public SessionDate(LocalDateTime startDate, LocalDateTime endDate) { this.startDate = startDate; this.endDate = endDate; } + + public LocalDateTime getStartDate() { + return startDate; + } + + public LocalDateTime getEndDate() { + return endDate; + } } diff --git a/src/main/java/nextstep/courses/domain/SessionRepository.java b/src/main/java/nextstep/courses/domain/SessionRepository.java new file mode 100644 index 0000000000..9c9648bee3 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/SessionRepository.java @@ -0,0 +1,6 @@ +package nextstep.courses.domain; + +public interface SessionRepository { + long save(Session session); + Session findById(Long id); +} diff --git a/src/main/java/nextstep/courses/domain/SessionState.java b/src/main/java/nextstep/courses/domain/SessionState.java index 24ab0e03d1..5210cd55e6 100644 --- a/src/main/java/nextstep/courses/domain/SessionState.java +++ b/src/main/java/nextstep/courses/domain/SessionState.java @@ -3,6 +3,9 @@ public enum SessionState { READY("READY"), START("START"), END("END"); + public String getState() { + return state; + } public String state; diff --git a/src/main/java/nextstep/courses/domain/sessionimage/ImageCapacity.java b/src/main/java/nextstep/courses/domain/image/ImageCapacity.java similarity index 75% rename from src/main/java/nextstep/courses/domain/sessionimage/ImageCapacity.java rename to src/main/java/nextstep/courses/domain/image/ImageCapacity.java index 61b7857699..5375a78cc5 100644 --- a/src/main/java/nextstep/courses/domain/sessionimage/ImageCapacity.java +++ b/src/main/java/nextstep/courses/domain/image/ImageCapacity.java @@ -1,6 +1,6 @@ -package nextstep.courses.domain.sessionimage; +package nextstep.courses.domain.image; -import nextstep.courses.Exception.CustomException; +import nextstep.courses.exception.CustomException; public class ImageCapacity { @@ -9,6 +9,7 @@ public class ImageCapacity { private final int imageSize; + public ImageCapacity(int imageSize) { validateSize(imageSize); this.imageSize = imageSize; @@ -19,4 +20,9 @@ private void validateSize(int imageSize) { throw CustomException.OVER_MAX_IMAGE_CAPACITY; } } + + public int getImageSize() { + return imageSize; + } + } diff --git a/src/main/java/nextstep/courses/domain/sessionimage/ImageSize.java b/src/main/java/nextstep/courses/domain/image/ImageSize.java similarity index 81% rename from src/main/java/nextstep/courses/domain/sessionimage/ImageSize.java rename to src/main/java/nextstep/courses/domain/image/ImageSize.java index c361b9e1b0..55529f74e1 100644 --- a/src/main/java/nextstep/courses/domain/sessionimage/ImageSize.java +++ b/src/main/java/nextstep/courses/domain/image/ImageSize.java @@ -1,6 +1,6 @@ -package nextstep.courses.domain.sessionimage; +package nextstep.courses.domain.image; -import nextstep.courses.Exception.CustomException; +import nextstep.courses.exception.CustomException; import java.math.BigDecimal; import java.math.RoundingMode; @@ -29,4 +29,13 @@ private void validateSize(int width, int height) { throw CustomException.IMAGE_PERCENT_ERROR; } } + + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } } diff --git a/src/main/java/nextstep/courses/domain/sessionimage/ImageType.java b/src/main/java/nextstep/courses/domain/image/ImageType.java similarity index 78% rename from src/main/java/nextstep/courses/domain/sessionimage/ImageType.java rename to src/main/java/nextstep/courses/domain/image/ImageType.java index 17f5371374..bf8db48ece 100644 --- a/src/main/java/nextstep/courses/domain/sessionimage/ImageType.java +++ b/src/main/java/nextstep/courses/domain/image/ImageType.java @@ -1,6 +1,6 @@ -package nextstep.courses.domain.sessionimage; +package nextstep.courses.domain.image; -import nextstep.courses.Exception.CustomException; +import nextstep.courses.exception.CustomException; import java.util.Arrays; @@ -12,7 +12,6 @@ public enum ImageType { png, svg; - public static ImageType validateType(String imageType) { return Arrays.stream(values()).filter(type -> type.name().equals(imageType)) .findFirst() diff --git a/src/main/java/nextstep/courses/domain/sessionimage/SessionImage.java b/src/main/java/nextstep/courses/domain/image/SessionImage.java similarity index 60% rename from src/main/java/nextstep/courses/domain/sessionimage/SessionImage.java rename to src/main/java/nextstep/courses/domain/image/SessionImage.java index 793d110a2a..7358f4ca51 100644 --- a/src/main/java/nextstep/courses/domain/sessionimage/SessionImage.java +++ b/src/main/java/nextstep/courses/domain/image/SessionImage.java @@ -1,4 +1,4 @@ -package nextstep.courses.domain.sessionimage; +package nextstep.courses.domain.image; public class SessionImage { @@ -6,11 +6,23 @@ public class SessionImage { private final ImageType type; private final ImageSize size; - public SessionImage(ImageCapacity capacity, ImageType type, ImageSize size) { ImageType.validateType(type.name()); this.capacity = capacity; this.type = type; this.size = size; } + + public ImageCapacity getCapacity() { + return capacity; + } + + public ImageType getType() { + return type; + } + + public ImageSize getSize() { + return size; + } + } diff --git a/src/main/java/nextstep/courses/Exception/CustomException.java b/src/main/java/nextstep/courses/exception/CustomException.java similarity index 52% rename from src/main/java/nextstep/courses/Exception/CustomException.java rename to src/main/java/nextstep/courses/exception/CustomException.java index 61a5f99aed..55cf73a2a9 100644 --- a/src/main/java/nextstep/courses/Exception/CustomException.java +++ b/src/main/java/nextstep/courses/exception/CustomException.java @@ -1,20 +1,20 @@ -package nextstep.courses.Exception; +package nextstep.courses.exception; public class CustomException extends RuntimeException { - public static final CustomException NOT_ALLOWED_DATE = new CustomException(ResponseType.NOT_ALLOWED_DATE_MESSAGE); - public static final CustomException OVER_MAX_IMAGE_CAPACITY = new CustomException(ResponseType.OVER_MAX_IMAGE_CAPACITY); - public static final CustomException IMAGE_SIZE_ERROR = new CustomException(ResponseType.IMAGE_SIZE_ERROR); - public static final CustomException IMAGE_PERCENT_ERROR = new CustomException(ResponseType.IMAGE_PERCENT_ERROR); - public static final CustomException NOT_ALLOWED_PREMIUM_AMOUNT = new CustomException(ResponseType.NOT_ALLOWED_PREMIUM_AMOUNT); - public static final CustomException NOT_ALLOWED_FREE_AMOUNT = new CustomException(ResponseType.NOT_ALLOWED_FREE_AMOUNT); - public static final CustomException NOT_MATCHING_SESSION_AMOUNT = new CustomException(ResponseType.NOT_MACHING_SESSION_AMOUNT); - public static final CustomException MAX_STUDENTS_OVER = new CustomException(ResponseType.MAX_STUDENTS_OVER); - public static final CustomException INVALID_SESSION_STATE = new CustomException(ResponseType.INVALID_SESSION_STATE); - public static final CustomException INVALID_IMAGE_TYPE = new CustomException(ResponseType.INVALID_IMAGE_TYPE); + public static final CustomException NOT_ALLOWED_DATE = new CustomException("허용되지 않은 시작날짜입니다."); + public static final CustomException OVER_MAX_IMAGE_CAPACITY = new CustomException("이미지 사이즈는 1MB 이하여야 합니다."); + public static final CustomException IMAGE_SIZE_ERROR = new CustomException("이미지 사이즈 오류입니다."); + public static final CustomException IMAGE_PERCENT_ERROR = new CustomException("이미지 비율 오류"); + public static final CustomException NOT_ALLOWED_PREMIUM_AMOUNT = new CustomException("유료강의 금액은 0원이 될 수 없습니다."); + public static final CustomException NOT_ALLOWED_FREE_AMOUNT = new CustomException("무료 강의 금액이 0원이 아닙니다."); + public static final CustomException NOT_MATCHING_SESSION_AMOUNT = new CustomException("강의 금액과 맞지 않습니다."); + public static final CustomException MAX_STUDENTS_OVER = new CustomException("수강인원 초과"); + public static final CustomException INVALID_SESSION_STATE = new CustomException("강의신청 기간이 아닙니다."); + public static final CustomException INVALID_IMAGE_TYPE = new CustomException("이미지 타입이 올바르지 않습니다."); - public CustomException(ResponseType responseType) { - super(responseType.message); + public CustomException(String message) { + super(message); } @Override @@ -22,5 +22,4 @@ public synchronized Throwable fillInStackTrace() { return this; } - } diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java index f9122cbe33..e71bf2ea91 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java @@ -2,40 +2,69 @@ import nextstep.courses.domain.Course; import nextstep.courses.domain.CourseRepository; -import org.springframework.jdbc.core.JdbcOperations; +import nextstep.courses.domain.CourseSessionRepository; +import nextstep.courses.domain.Session; import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.stereotype.Repository; import java.sql.Timestamp; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; @Repository("courseRepository") public class JdbcCourseRepository implements CourseRepository { - private JdbcOperations jdbcTemplate; + private NamedParameterJdbcTemplate namedParameterJdbcTemplate; + private JdbcSessionRepository jdbcSessionRepository; + private CourseSessionRepository courseSessionRepository; - public JdbcCourseRepository(JdbcOperations jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; + public JdbcCourseRepository(NamedParameterJdbcTemplate namedParameterJdbcTemplate, JdbcSessionRepository sessionRepository, CourseSessionRepository courseSessionRepository) { + this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; + this.jdbcSessionRepository = sessionRepository; + this.courseSessionRepository = courseSessionRepository; } @Override public int save(Course course) { - String sql = "insert into course (title, creator_id, created_at) values(?, ?, ?)"; - return jdbcTemplate.update(sql, course.getTitle(), course.getCreatorId(), course.getCreatedAt()); + String sql = "insert into course (title, creator_id, created_at, class_number) " + + "values(:title, :creatorId, :createdAt, :classNumber)"; + SqlParameterSource parameterSource = new MapSqlParameterSource() + .addValue("title", course.getTitle()) + .addValue("creatorId", course.getCreatorId()) + .addValue("createdAt", course.getCreatedAt()) + .addValue("classNumber", course.getClassNumber()); + + return namedParameterJdbcTemplate.update(sql, parameterSource); } @Override public Course findById(Long id) { - String sql = "select id, title, creator_id, created_at, updated_at from course where id = ?"; - RowMapper rowMapper = (rs, rowNum) -> new Course( - rs.getLong(1), - rs.getString(2), - rs.getLong(3), - toLocalDateTime(rs.getTimestamp(4)), - toLocalDateTime(rs.getTimestamp(5))); - return jdbcTemplate.queryForObject(sql, rowMapper, id); + String sql = "select id, title, creator_id, created_at, updated_at, class_number from course where id = :id"; + List sessions = getSessions(id);/*getSessions(id);*/ + RowMapper ROW_MAPPER = (rs, rowNum) -> new Course( + rs.getLong("id"), + rs.getString("title"), + rs.getLong("creator_id"), + toLocalDateTime(rs.getTimestamp("created_at")), + toLocalDateTime(rs.getTimestamp("updated_at")), + rs.getInt("class_number"), + sessions + ); + MapSqlParameterSource parameterSource = new MapSqlParameterSource().addValue("id",id); + return namedParameterJdbcTemplate.queryForObject(sql, parameterSource, ROW_MAPPER); + } + + private List getSessions(Long id) { + List sessionIds = courseSessionRepository.findByCourseId(id); + return sessionIds.stream().map(jdbcSessionRepository::findById).collect(Collectors.toList()); } - private LocalDateTime toLocalDateTime(Timestamp timestamp) { + + private static LocalDateTime toLocalDateTime(Timestamp timestamp) { if (timestamp == null) { return null; } diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcCourseSesisonRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcCourseSesisonRepository.java new file mode 100644 index 0000000000..af3bca980a --- /dev/null +++ b/src/main/java/nextstep/courses/infrastructure/JdbcCourseSesisonRepository.java @@ -0,0 +1,37 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.CourseSessionRepository; +import org.springframework.jdbc.core.JdbcOperations; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +public class JdbcCourseSesisonRepository implements CourseSessionRepository { + + private final JdbcOperations jdbcTemplate; + + public JdbcCourseSesisonRepository(JdbcOperations jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + + @Override + public int save(Long courseId, List sessionIds) { + String sql = "insert into course_session (course_id, session_id) values(?,?)"; + int totalInsert = 0; + for (Long sessionId : sessionIds) { + int count = jdbcTemplate.update(sql, courseId, sessionId); + totalInsert += count; + } + return totalInsert; + } + + @Override + public List findByCourseId(Long courseId) { + String sql = "select session_id from course_session where course_id = ?"; + RowMapper rowMapper = (rs, rowNum) -> rs.getLong("session_id"); + return jdbcTemplate.query(sql,rowMapper, courseId); + } + +} 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..9d61bab101 --- /dev/null +++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java @@ -0,0 +1,120 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.*; +import nextstep.courses.domain.image.ImageCapacity; +import nextstep.courses.domain.image.ImageSize; +import nextstep.courses.domain.image.ImageType; +import nextstep.courses.domain.image.SessionImage; +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.Timestamp; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Repository() +public class JdbcSessionRepository implements SessionRepository { + private final JdbcOperations jdbcTemplate; + + public JdbcSessionRepository(JdbcOperations jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Override + public long save(Session session) { + String sql = "insert into session (session_amount," + + " is_premium," + + " session_state," + + " image_capacity,image_type, image_width, image_height, max_student_count," + + "start_date, end_date" + + ") values(?,?,?,?,?,?,?,?,?,?)"; + KeyHolder keyHolder = new GeneratedKeyHolder(); + + jdbcTemplate.update(connection -> { + PreparedStatement ps = connection.prepareStatement(sql, new String[]{"id"}); + ps.setInt(1, session.getPricingType().getSessionAmount()); + ps.setBoolean(2, session.getPricingType().getIsPremium()); + ps.setString(3, session.getState().getState()); + ps.setInt(4, session.getImage().getCapacity().getImageSize()); + ps.setString(5, session.getImage().getType().toString()); + ps.setInt(6, session.getImage().getSize().getWidth()); + ps.setInt(7, session.getImage().getSize().getHeight()); + ps.setInt(8, session.getMaxStudentCount()); + ps.setTimestamp(9, Timestamp.valueOf(session.getDate().getStartDate())); + ps.setTimestamp(10, Timestamp.valueOf(session.getDate().getEndDate())); + return ps; + }, keyHolder); + + long pk = keyHolder.getKey().longValue(); + + for (Long studentId : session.getStudents()) { + saveSessionStudent(pk, studentId); + } + return pk; + } + + private long saveSessionStudent(long pk, Long studentId) { + + String sql = "insert into session_students (id, session_id" + + ") values(?,?);"; + KeyHolder keyHolder = new GeneratedKeyHolder(); + + jdbcTemplate.update(connection -> { + PreparedStatement ps = connection.prepareStatement(sql, new String[]{"id"}); + ps.setLong(1, studentId); + ps.setLong(2, pk); + return ps; + }, keyHolder); + + return keyHolder.getKey().longValue(); + } + + @Override + public Session findById(Long id) { + String sql = "select id," + + " session_amount," + + " is_premium," + + " session_state," + + " image_capacity," + + " image_type," + + " image_width, image_height, max_student_count, start_date, end_date, course_id from session where id = ?"; + RowMapper rowMapper = (rs, rowNum) -> { + long pk = rs.getLong(1); + List students = findStudents(pk); + + return new Session(pk, + students, + new PricingType( rs.getBoolean(3), rs.getInt(2)), + SessionState.valueOf(rs.getString(4)), + new SessionImage(new ImageCapacity(rs.getInt(5)), ImageType.valueOf(rs.getString(6)), new ImageSize(rs.getInt(7),rs.getInt(8))), + rs.getInt(9), + new SessionDate(toLocalDateTime(rs.getTimestamp(10)), toLocalDateTime(rs.getTimestamp(11))) + ,rs.getLong(12) ); + }; + + return jdbcTemplate.queryForObject(sql, rowMapper, id); + } + + private List findStudents(long pk) { + String sql = "select id from session_students where session_id = ?"; + return jdbcTemplate.query(sql, rs -> { + List studentIdList = new ArrayList<>(); + while (rs.next()) { + studentIdList.add(rs.getLong(1)); + } + return studentIdList; + }, pk); + } + + private LocalDateTime toLocalDateTime(Timestamp timestamp) { + if (timestamp == null) { + return null; + } + return timestamp.toLocalDateTime(); + } +} diff --git a/src/main/java/nextstep/payments/domain/Payment.java b/src/main/java/nextstep/payments/domain/Payment.java index efde967573..6ff7a3136a 100644 --- a/src/main/java/nextstep/payments/domain/Payment.java +++ b/src/main/java/nextstep/payments/domain/Payment.java @@ -11,7 +11,7 @@ public class Payment { private Long sessionId; // 결제한 사용자 아이디 - private NsUser nsUser; + private Long nsUserId; // 결제 금액 private Long amount; @@ -21,16 +21,16 @@ public class Payment { public Payment() { } - public Payment(String id, Long sessionId, NsUser nsUser, Long amount) { + public Payment(String id, Long sessionId, Long nsUserId, Long amount) { this.id = id; this.sessionId = sessionId; - this.nsUser = nsUser; + this.nsUserId = nsUserId; this.amount = amount; this.createdAt = LocalDateTime.now(); } - public NsUser payingUser() { - return nsUser; + public Long payingUser() { + return nsUserId; } public boolean matchingAmount(int sessionAmount) { diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 8d5a988c8b..ad148699c0 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -4,9 +4,42 @@ create table course ( creator_id bigint not null, created_at timestamp not null, updated_at timestamp, + class_number bigint not null, primary key (id) ); +create table session ( + id bigint generated by default as identity, + session_amount int not null, + is_premium boolean not null, + session_state varchar not null, + image_capacity int, + image_type varchar, + image_width int, + image_height int, + max_student_count int, + start_date timestamp, + end_date timestamp, + course_id int, + primary key (id) +); + +create table session_students ( + id bigint not null, + session_id bigint, + primary key (id), + foreign key (session_id) REFERENCES SESSION(id), + constraint unique_registration unique (id, session_id) +); + +create table course_session ( + id bigint generated by default as identity + course_id bigint, + sesison_id bigint, + primary key (id) +) + + create table ns_user ( id bigint generated by default as identity, user_id varchar(20) not null, diff --git a/src/test/java/nextstep/courses/domain/ImageCapacityTest.java b/src/test/java/nextstep/courses/domain/ImageCapacityTest.java index 6970ed4680..596a8eff32 100644 --- a/src/test/java/nextstep/courses/domain/ImageCapacityTest.java +++ b/src/test/java/nextstep/courses/domain/ImageCapacityTest.java @@ -1,16 +1,14 @@ package nextstep.courses.domain; -import nextstep.courses.Exception.CustomException; -import nextstep.courses.domain.sessionimage.ImageCapacity; -import nextstep.courses.domain.sessionimage.ImageSize; -import nextstep.courses.domain.sessionimage.ImageType; -import nextstep.courses.domain.sessionimage.SessionImage; -import org.junit.jupiter.api.BeforeEach; +import nextstep.courses.exception.CustomException; +import nextstep.courses.domain.image.ImageCapacity; +import nextstep.courses.domain.image.ImageSize; +import nextstep.courses.domain.image.ImageType; +import nextstep.courses.domain.image.SessionImage; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; diff --git a/src/test/java/nextstep/courses/domain/ImageSizeTest.java b/src/test/java/nextstep/courses/domain/ImageSizeTest.java index 988f55fc81..2efbab02fb 100644 --- a/src/test/java/nextstep/courses/domain/ImageSizeTest.java +++ b/src/test/java/nextstep/courses/domain/ImageSizeTest.java @@ -1,11 +1,10 @@ package nextstep.courses.domain; -import nextstep.courses.Exception.CustomException; -import nextstep.courses.domain.sessionimage.ImageSize; +import nextstep.courses.exception.CustomException; +import nextstep.courses.domain.image.ImageSize; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.assertThatThrownBy; public class ImageSizeTest { diff --git a/src/test/java/nextstep/courses/domain/ImageTypeTest.java b/src/test/java/nextstep/courses/domain/ImageTypeTest.java index b97de82ec0..e4417b0ac0 100644 --- a/src/test/java/nextstep/courses/domain/ImageTypeTest.java +++ b/src/test/java/nextstep/courses/domain/ImageTypeTest.java @@ -1,14 +1,13 @@ package nextstep.courses.domain; -import nextstep.courses.Exception.CustomException; -import nextstep.courses.domain.sessionimage.ImageCapacity; -import nextstep.courses.domain.sessionimage.ImageSize; -import nextstep.courses.domain.sessionimage.ImageType; -import nextstep.courses.domain.sessionimage.SessionImage; +import nextstep.courses.exception.CustomException; +import nextstep.courses.domain.image.ImageCapacity; +import nextstep.courses.domain.image.ImageSize; +import nextstep.courses.domain.image.ImageType; +import nextstep.courses.domain.image.SessionImage; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; diff --git a/src/test/java/nextstep/courses/domain/SessionDateTest.java b/src/test/java/nextstep/courses/domain/SessionDateTest.java index 0e503ac3a8..bc230620ef 100644 --- a/src/test/java/nextstep/courses/domain/SessionDateTest.java +++ b/src/test/java/nextstep/courses/domain/SessionDateTest.java @@ -1,6 +1,6 @@ package nextstep.courses.domain; -import nextstep.courses.Exception.CustomException; +import nextstep.courses.exception.CustomException; import org.junit.jupiter.api.Test; import java.time.LocalDateTime; diff --git a/src/test/java/nextstep/courses/domain/SessionTest.java b/src/test/java/nextstep/courses/domain/SessionTest.java index 885edbd8f0..67adf97577 100644 --- a/src/test/java/nextstep/courses/domain/SessionTest.java +++ b/src/test/java/nextstep/courses/domain/SessionTest.java @@ -1,18 +1,16 @@ package nextstep.courses.domain; -import nextstep.courses.Exception.CustomException; -import nextstep.courses.domain.sessionimage.ImageCapacity; -import nextstep.courses.domain.sessionimage.ImageSize; -import nextstep.courses.domain.sessionimage.ImageType; -import nextstep.courses.domain.sessionimage.SessionImage; +import nextstep.courses.exception.CustomException; +import nextstep.courses.domain.image.ImageCapacity; +import nextstep.courses.domain.image.ImageSize; +import nextstep.courses.domain.image.ImageType; +import nextstep.courses.domain.image.SessionImage; import nextstep.payments.domain.Payment; -import nextstep.users.domain.NsUser; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.time.LocalDateTime; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -34,9 +32,8 @@ public class SessionTest { public void setUp() { sessionDate = new SessionDate(LocalDateTime.now(), LocalDateTime.now().plusDays(2)); - NsUser nsUser = new NsUser(1L, "test", "pwd", "wonsuk", "email"); - matchPayment = new Payment("test", 1L, nsUser, 10000L); - unMatchPayment = new Payment("test", 1L, nsUser, 9000L); + matchPayment = new Payment("test", 1L, 1L, 10000L); + unMatchPayment = new Payment("test", 1L, 1L, 9000L); imageCapacity = new ImageCapacity(IMAGE_CAPACITY); imageSize = new ImageSize(WIDTH, HEIGHT); diff --git a/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java index f087fc0ad2..1827ea9e3c 100644 --- a/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java +++ b/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java @@ -8,7 +8,7 @@ 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 org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import static org.assertj.core.api.Assertions.assertThat; @@ -17,13 +17,14 @@ public class CourseRepositoryTest { private static final Logger LOGGER = LoggerFactory.getLogger(CourseRepositoryTest.class); @Autowired - private JdbcTemplate jdbcTemplate; + private NamedParameterJdbcTemplate namedParameterJdbcTemplate; private CourseRepository courseRepository; + private JdbcSessionRepository jdbcSessionRepository; @BeforeEach void setUp() { - courseRepository = new JdbcCourseRepository(jdbcTemplate); + courseRepository = new JdbcCourseRepository(namedParameterJdbcTemplate, jdbcSessionRepository); } @Test 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..0a2d38f0be --- /dev/null +++ b/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java @@ -0,0 +1,53 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.*; +import nextstep.courses.domain.image.ImageCapacity; +import nextstep.courses.domain.image.ImageSize; +import nextstep.courses.domain.image.ImageType; +import nextstep.courses.domain.image.SessionImage; +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.assertThat; + +@JdbcTest +public class SessionRepositoryTest { + private static final Logger LOGGER = LoggerFactory.getLogger(SessionRepositoryTest.class); + + @Autowired + private JdbcTemplate jdbcTemplate; + + private SessionRepository sessionRepository; + + @BeforeEach + void setUp() { + sessionRepository = new JdbcSessionRepository(jdbcTemplate); + } + + @Test + void crud() { + + List testStudents = List.of(1L, 2L, 3L); + Session testSession = new Session(1L, + testStudents, + new PricingType(false, 0), + SessionState.valueOf("START"), + new SessionImage(new ImageCapacity(1024), ImageType.valueOf("gif"), + new ImageSize(300, 200)), + 10, + new SessionDate(LocalDateTime.now(), LocalDateTime.now().plusDays(2)),1L ); + + Long sessionPk = sessionRepository.save(testSession); + Session savedSession = sessionRepository.findById(sessionPk); + assertThat(savedSession.getStudents()).hasSameElementsAs(testStudents); + LOGGER.debug("Session: {}", savedSession); + } +}