diff --git a/src/main/java/nextstep/courses/domain/Capacity.java b/src/main/java/nextstep/courses/domain/Capacity.java new file mode 100644 index 000000000..0cc606ab2 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/Capacity.java @@ -0,0 +1,22 @@ +package nextstep.courses.domain; + +import nextstep.courses.exception.BusinessInvalidValueException; + +public class Capacity { + private final int capacity; + + public Capacity(int capacity) { + this.capacity = capacity; + } + + public void validateCapacity(int count) { + if (count >= capacity) { + throw new BusinessInvalidValueException("최대수강인원을 초과했습니다."); + } + } + + public int capacity() { + return capacity; + } +} + diff --git a/src/main/java/nextstep/courses/domain/Course.java b/src/main/java/nextstep/courses/domain/Course.java index 0f6971604..f76223d3a 100644 --- a/src/main/java/nextstep/courses/domain/Course.java +++ b/src/main/java/nextstep/courses/domain/Course.java @@ -1,25 +1,31 @@ package nextstep.courses.domain; +import nextstep.courses.utils.BaseEntity; + import java.time.LocalDateTime; -public class Course { +public class Course extends BaseEntity { private Long id; private String title; private Long creatorId; - private LocalDateTime createdAt; - - private LocalDateTime updatedAt; - public Course() { } + public Course(Long id) { + this.id = id; + } + public Course(String title, Long creatorId) { this(0L, title, creatorId, LocalDateTime.now(), null); } + public Course(Long id, String title, Long creatorId) { + this(id, title, creatorId, LocalDateTime.now(), null); + } + public Course(Long id, String title, Long creatorId, LocalDateTime createdAt, LocalDateTime updatedAt) { this.id = id; this.title = title; @@ -28,6 +34,10 @@ public Course(Long id, String title, Long creatorId, LocalDateTime createdAt, Lo this.updatedAt = updatedAt; } + public Long id() { + return id; + } + public String getTitle() { return title; } diff --git a/src/main/java/nextstep/courses/domain/FreeSession.java b/src/main/java/nextstep/courses/domain/FreeSession.java deleted file mode 100644 index 8ae90c460..000000000 --- a/src/main/java/nextstep/courses/domain/FreeSession.java +++ /dev/null @@ -1,18 +0,0 @@ -package nextstep.courses.domain; - -import nextstep.users.domain.NsUser; - -import java.time.LocalDateTime; - -public class FreeSession extends Session { - - public FreeSession(Long id, LocalDateTime beginDt, LocalDateTime endDt, SessionCover sessionCover, Course course) { - super(id, beginDt, endDt, sessionCover, course); - } - - @Override - public void enroll(NsUser participant, Long amount) { - validateStatus(); - this.participants.add(participant); - } -} diff --git a/src/main/java/nextstep/courses/domain/PaidSession.java b/src/main/java/nextstep/courses/domain/PaidSession.java deleted file mode 100644 index 195b646f5..000000000 --- a/src/main/java/nextstep/courses/domain/PaidSession.java +++ /dev/null @@ -1,45 +0,0 @@ -package nextstep.courses.domain; - -import nextstep.courses.exception.BusinessInvalidValueException; -import nextstep.users.domain.NsUser; - -import java.time.LocalDateTime; - -public class PaidSession extends Session { - private final int capacity; - private final long price; - - public PaidSession(Long id, LocalDateTime beginDt, LocalDateTime endDt, SessionCover sessionCover, Course course, int capacity, long price) { - super(id, beginDt, endDt, sessionCover, course); - this.capacity = capacity; - this.price = price; - } - - @Override - public void enroll(NsUser participant, Long amount) { - validateCapacity(); - validatePrice(amount); - validateStatus(); - this.participants.add(participant); - } - - private void validateCapacity() { - if (this.participants.size() >= capacity) { - throw new BusinessInvalidValueException("최대수강인원을 초과했습니다."); - } - } - - private void validatePrice(long price) { - if (this.price != price) { - throw new BusinessInvalidValueException("강의 가격이 변동되었습니다."); - } - } - - public long price() { - return price; - } - - public int capacity() { - return capacity; - } -} diff --git a/src/main/java/nextstep/courses/domain/Period.java b/src/main/java/nextstep/courses/domain/Period.java index 4a24dacf9..219b3c412 100644 --- a/src/main/java/nextstep/courses/domain/Period.java +++ b/src/main/java/nextstep/courses/domain/Period.java @@ -10,4 +10,12 @@ public Period(LocalDateTime beginDt, LocalDateTime endDt) { this.beginDt = beginDt; this.endDt = endDt; } + + public LocalDateTime getBeginDt() { + return beginDt; + } + + public LocalDateTime getEndDt() { + return endDt; + } } diff --git a/src/main/java/nextstep/courses/domain/Price.java b/src/main/java/nextstep/courses/domain/Price.java new file mode 100644 index 000000000..c87140dd0 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/Price.java @@ -0,0 +1,21 @@ +package nextstep.courses.domain; + +import nextstep.courses.exception.BusinessInvalidValueException; + +public class Price { + private final long price; + + public Price(long price) { + this.price = price; + } + + public void validatePrice(long price) { + if (this.price != price) { + throw new BusinessInvalidValueException("강의 가격이 변동되었습니다."); + } + } + + public long price() { + return price; + } +} diff --git a/src/main/java/nextstep/courses/domain/Registration.java b/src/main/java/nextstep/courses/domain/Registration.java index 4d0b22577..95e200cb6 100644 --- a/src/main/java/nextstep/courses/domain/Registration.java +++ b/src/main/java/nextstep/courses/domain/Registration.java @@ -1,13 +1,50 @@ package nextstep.courses.domain; +import nextstep.courses.utils.BaseEntity; import nextstep.users.domain.NsUser; -public class Registration { +import java.time.LocalDateTime; - public Registration() { +public class Registration extends BaseEntity { + private Long id = null; + private final NsUser user; + private final Session session; + private final Long amount; + + public Registration(Long id, NsUser user, Session session, Long amount, LocalDateTime createdAt, LocalDateTime updatedAt) { + this.id = id; + this.user = user; + this.session = session; + this.amount = amount; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } + + public Registration(NsUser user, Session session, Long amount) { + this.user = user; + this.session = session; + this.amount = amount; } - public void register(NsUser user, Session session, Long amount) { + public static Registration register(NsUser user, Session session, Long amount) { + Registration registration = new Registration(user, session, amount); session.enroll(user, amount); + return registration; + } + + public Long id() { + return id; + } + + public NsUser nsUser() { + return user; + } + + public Session session() { + return session; + } + + public Long amount() { + return amount; } } diff --git a/src/main/java/nextstep/courses/domain/Semester.java b/src/main/java/nextstep/courses/domain/Semester.java new file mode 100644 index 000000000..082e64053 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/Semester.java @@ -0,0 +1,11 @@ +package nextstep.courses.domain; + +import nextstep.courses.utils.BaseEntity; + +public class Semester extends BaseEntity { + private final Course course; + + public Semester(Course course) { + this.course = course; + } +} diff --git a/src/main/java/nextstep/courses/domain/Session.java b/src/main/java/nextstep/courses/domain/Session.java index 99e79af17..32e4d66b2 100644 --- a/src/main/java/nextstep/courses/domain/Session.java +++ b/src/main/java/nextstep/courses/domain/Session.java @@ -2,40 +2,117 @@ import nextstep.courses.enums.SessionStatus; import nextstep.courses.exception.BusinessInvalidValueException; +import nextstep.courses.utils.BaseEntity; import nextstep.users.domain.NsUser; import java.time.LocalDateTime; -import java.util.ArrayList; import java.util.List; -public abstract class Session { - final Long id; - final Course course; - final SessionCover sessionCover; - final Period period; - SessionStatus status = SessionStatus.PREPARE; - final List<NsUser> participants = new ArrayList<>(); +public class Session extends BaseEntity { + public static long FREE_PRICE = 0L; + public static int MAX_CAPACITY = Integer.MAX_VALUE; + private final Long id; + private Course course; + private SessionCover sessionCover; + private Period period; + private Capacity capacity; + private Price price; - public Session(Long id, LocalDateTime beginDt, LocalDateTime endDt, SessionCover sessionCover, Course course) { + private SessionStatus status = SessionStatus.PREPARE; + private List<NsUser> participants; + + public Session(Long id) { + this.id = id; + } + + private Session(Long id, LocalDateTime beginDt, LocalDateTime endDt, SessionCover sessionCover, Course course, Capacity capacity, Price price, List<NsUser> participants) { + this.id = id; + this.period = new Period(beginDt, endDt); + this.sessionCover = sessionCover; + this.course = course; + this.capacity = capacity; + this.price = price; + this.participants = participants; + } + + public Session(Long id, LocalDateTime beginDt, LocalDateTime endDt, SessionStatus status, Capacity capacity, Price price, Course course, SessionCover sessionCover, LocalDateTime createdAt, LocalDateTime updatedAt) { this.id = id; this.period = new Period(beginDt, endDt); + this.status = status; this.sessionCover = sessionCover; this.course = course; + this.capacity = capacity; + this.price = price; + this.createdAt = createdAt; + this.updatedAt = updatedAt; } - public abstract void enroll(NsUser participant, Long amount); + public static Session fromSessionForFree(Session session, SessionCover sessionCover, Course course, List<NsUser> participants) { + return Session.ofFree(session.id, session.period.getBeginDt(), session.period.getEndDt(), sessionCover, course, participants); + } + + public static Session fromSessionForPaid(Session session, SessionCover sessionCover, Course course, List<NsUser> participants) { + return Session.ofPaid(session.id, session.period.getBeginDt(), session.period.getEndDt(), sessionCover, course, session.price.price(), session.capacity.capacity(), participants); + } + + public static Session ofFree(Long id, LocalDateTime beginDt, LocalDateTime endDt, SessionCover sessionCover, Course course, List<NsUser> participants) { + return new Session(id, beginDt, endDt, sessionCover, course, new Capacity(MAX_CAPACITY), new Price(FREE_PRICE), participants); + } + + public static Session ofPaid(Long id, LocalDateTime beginDt, LocalDateTime endDt, SessionCover sessionCover, Course course, Long price, Integer capacity, List<NsUser> participants) { + return new Session(id, beginDt, endDt, sessionCover, course, new Capacity(capacity), new Price(price), participants); + } + + public static Session fromSession(Session session, SessionCover sessionCover, Course course, List<NsUser> participants) { + if (session.price.equals(0L)) { + return fromSessionForFree(session, sessionCover, course, participants); + } + return fromSessionForPaid(session, sessionCover, course, participants); + } public void startEnrollment() { this.status = SessionStatus.ENROLL; } - public void validateStatus() { + public void enroll(NsUser participant, Long amount) { + capacity.validateCapacity(participants.size()); + price.validatePrice(amount); + validateStatus(); + this.participants.add(participant); + } + + + private void validateStatus() { if (SessionStatus.ENROLL != this.status) { throw new BusinessInvalidValueException("수강신청 가능한 상태가 아닙니다."); } } + public Price price() { + return price; + } + + public Capacity capacity() { + return capacity; + } + public Long id() { return id; } + + public Period period() { + return period; + } + + public Course course() { + return course; + } + + public SessionCover sessionCover() { + return sessionCover; + } + + public String status() { + return this.status.name(); + } } diff --git a/src/main/java/nextstep/courses/domain/SessionCover.java b/src/main/java/nextstep/courses/domain/SessionCover.java index 3f2822d5e..c9843c4f7 100644 --- a/src/main/java/nextstep/courses/domain/SessionCover.java +++ b/src/main/java/nextstep/courses/domain/SessionCover.java @@ -1,10 +1,31 @@ package nextstep.courses.domain; -public class SessionCover { +import nextstep.courses.utils.BaseEntity; + +import java.time.LocalDateTime; + +public class SessionCover extends BaseEntity { public static final double RATIO = (double) 3 / 2; - private final byte[] image; - public SessionCover(int width, int height, long size, byte[] image) { + private Long id; + private byte[] image; + + public SessionCover(Long id) { + this.id = id; + } + + public SessionCover(byte[] image) { + this.image = image; + } + + public SessionCover(Long id, byte[] image, LocalDateTime createdAt, LocalDateTime updatedAt) { + this.id = id; + this.image = image; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } + + public SessionCover(long id, int width, int height, long size, byte[] image) { validateSize(size); validatePixel(width, height); validateRatio(width, height); @@ -12,6 +33,7 @@ public SessionCover(int width, int height, long size, byte[] image) { throw new IllegalArgumentException("이미지 값이 없습니다."); } this.image = image; + this.id = id; } private static void validateSize(long size) { @@ -35,4 +57,8 @@ private static void validateRatio(int width, int height) { public byte[] image() { return image; } + + public Long id() { + return id; + } } diff --git a/src/main/java/nextstep/courses/domain/Symester.java b/src/main/java/nextstep/courses/domain/Symester.java deleted file mode 100644 index e9963f91f..000000000 --- a/src/main/java/nextstep/courses/domain/Symester.java +++ /dev/null @@ -1,9 +0,0 @@ -package nextstep.courses.domain; - -public class Symester { - private final Course course; - - public Symester(Course course) { - this.course = course; - } -} diff --git a/src/main/java/nextstep/courses/domain/CourseRepository.java b/src/main/java/nextstep/courses/domain/repository/CourseRepository.java similarity index 54% rename from src/main/java/nextstep/courses/domain/CourseRepository.java rename to src/main/java/nextstep/courses/domain/repository/CourseRepository.java index 6aaeb638d..675e42f31 100644 --- a/src/main/java/nextstep/courses/domain/CourseRepository.java +++ b/src/main/java/nextstep/courses/domain/repository/CourseRepository.java @@ -1,4 +1,6 @@ -package nextstep.courses.domain; +package nextstep.courses.domain.repository; + +import nextstep.courses.domain.Course; public interface CourseRepository { int save(Course course); diff --git a/src/main/java/nextstep/courses/domain/repository/RegistrationRepository.java b/src/main/java/nextstep/courses/domain/repository/RegistrationRepository.java new file mode 100644 index 000000000..9dab72269 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/repository/RegistrationRepository.java @@ -0,0 +1,11 @@ +package nextstep.courses.domain.repository; + +import nextstep.courses.domain.Registration; + +import java.util.List; + +public interface RegistrationRepository { + int save(Registration registration); + + List<Registration> findAllBySessionId(Long sessionId); +} diff --git a/src/main/java/nextstep/courses/domain/repository/SessionCoverRepository.java b/src/main/java/nextstep/courses/domain/repository/SessionCoverRepository.java new file mode 100644 index 000000000..582ea641f --- /dev/null +++ b/src/main/java/nextstep/courses/domain/repository/SessionCoverRepository.java @@ -0,0 +1,9 @@ +package nextstep.courses.domain.repository; + +import nextstep.courses.domain.SessionCover; + +public interface SessionCoverRepository { + int save(SessionCover sessionCover); + + SessionCover findById(Long id); +} diff --git a/src/main/java/nextstep/courses/domain/repository/SessionRepository.java b/src/main/java/nextstep/courses/domain/repository/SessionRepository.java new file mode 100644 index 000000000..535cf1062 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/repository/SessionRepository.java @@ -0,0 +1,11 @@ +package nextstep.courses.domain.repository; + +import nextstep.courses.domain.Session; + +import java.util.Optional; + +public interface SessionRepository { + int save(Session session); + + Optional<Session> findById(Long id); +} diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java index f9122cbe3..99c8b5922 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java @@ -1,13 +1,12 @@ package nextstep.courses.infrastructure; import nextstep.courses.domain.Course; -import nextstep.courses.domain.CourseRepository; +import nextstep.courses.domain.repository.CourseRepository; 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 static nextstep.courses.utils.DateUtil.toLocalDateTime; @Repository("courseRepository") public class JdbcCourseRepository implements CourseRepository { @@ -35,10 +34,4 @@ public Course findById(Long id) { 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/courses/infrastructure/JdbcRegistrationRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcRegistrationRepository.java new file mode 100644 index 000000000..61b5c01ff --- /dev/null +++ b/src/main/java/nextstep/courses/infrastructure/JdbcRegistrationRepository.java @@ -0,0 +1,45 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.Registration; +import nextstep.courses.domain.Session; +import nextstep.courses.domain.repository.RegistrationRepository; +import nextstep.users.domain.NsUser; +import org.springframework.jdbc.core.JdbcOperations; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.List; + +import static nextstep.courses.utils.DateUtil.toLocalDateTime; + +@Repository("registrationRepository") +public class JdbcRegistrationRepository implements RegistrationRepository { + private JdbcOperations jdbcTemplate; + + public JdbcRegistrationRepository(JdbcOperations jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Override + public int save(Registration registration) { + String sql = "insert into registration (ns_user_id, session_id, amount, created_at) values(?, ?, ?, ?)"; + return jdbcTemplate.update(sql, registration.nsUser().getId(), registration.session().id(), registration.amount(), LocalDateTime.now()); + } + + @Override + public List<Registration> findAllBySessionId(Long sessionId) { + String sql = "select id, session_id, ns_user_id, amount, created_at, updated_at " + + "from registration r where session_id = ?"; + + RowMapper<Registration> rowMapper = (rs, rowNum) -> new Registration( + rs.getLong(1), + new NsUser(rs.getLong(2)), + new Session(rs.getLong(3)), + rs.getLong(4), + toLocalDateTime(rs.getTimestamp(5)), + toLocalDateTime(rs.getTimestamp(6))); + + return jdbcTemplate.query(sql, rowMapper, sessionId); + } +} diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcSessionCoverRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcSessionCoverRepository.java new file mode 100644 index 000000000..bc1de599c --- /dev/null +++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionCoverRepository.java @@ -0,0 +1,38 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.SessionCover; +import nextstep.courses.domain.repository.SessionCoverRepository; +import org.springframework.jdbc.core.JdbcOperations; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; + +import static nextstep.courses.utils.DateUtil.toLocalDateTime; + +@Repository("sessionCoverRepository") +public class JdbcSessionCoverRepository implements SessionCoverRepository { + private final JdbcOperations jdbcTemplate; + + public JdbcSessionCoverRepository(JdbcOperations jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Override + public int save(SessionCover sessionCover) { + String sql = "insert into session_cover (image, created_at, updated_at) values(?,?,?)"; + return jdbcTemplate.update(sql, sessionCover.image(), LocalDateTime.now(), LocalDateTime.now()); + } + + @Override + public SessionCover findById(Long id) { + String sql = "select id, image, created_at, updated_at from session_cover where id = ?"; + RowMapper<SessionCover> rowMapper = (rs, rowNum) -> new SessionCover( + rs.getLong(1), + rs.getBytes(2), + toLocalDateTime(rs.getTimestamp(3)), + toLocalDateTime(rs.getTimestamp(4))); + + return jdbcTemplate.queryForObject(sql, rowMapper, id); + } +} diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java new file mode 100644 index 000000000..c10eb3a94 --- /dev/null +++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java @@ -0,0 +1,52 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.*; +import nextstep.courses.domain.repository.SessionRepository; +import nextstep.courses.enums.SessionStatus; +import org.springframework.jdbc.core.JdbcOperations; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.Optional; + +import static nextstep.courses.utils.DateUtil.toLocalDateTime; + +@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 (begin_dt, end_dt, session_status, capacity, price, course_id, session_cover_id, created_at, updated_at) values(?, ?, ?, ?, ?, ?, ?, ? ,?)"; + return jdbcTemplate.update(sql, session.period().getBeginDt(), session.period().getBeginDt(), session.status(), session.capacity().capacity(), + session.price().price(), session.course().id(), session.sessionCover().id(), LocalDateTime.now(), LocalDateTime.now()); + } + + @Override + public Optional<Session> findById(Long id) { + String sql = "select id, begin_dt, end_dt, session_status, capacity, price, " + + "course_id, session_cover_id, created_at, updated_at " + + "from session where id = ?"; + + RowMapper<Session> rowMapper = (rs, rowNum) -> new Session( + rs.getLong(1), + toLocalDateTime(rs.getTimestamp(2)), + toLocalDateTime(rs.getTimestamp(3)), + SessionStatus.valueOf(rs.getString(4)), + new Capacity(rs.getInt(5)), + new Price(rs.getInt(6)), + new Course(rs.getLong(7)), + new SessionCover(rs.getLong(8)), + toLocalDateTime(rs.getTimestamp(9)), + toLocalDateTime(rs.getTimestamp(10)) + ); + + return Optional.of(jdbcTemplate.queryForObject(sql, rowMapper, id)); + } +} 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 000000000..b69a191fe --- /dev/null +++ b/src/main/java/nextstep/courses/service/SessionService.java @@ -0,0 +1,57 @@ +package nextstep.courses.service; + +import nextstep.courses.domain.Course; +import nextstep.courses.domain.Registration; +import nextstep.courses.domain.Session; +import nextstep.courses.domain.SessionCover; +import nextstep.courses.domain.repository.CourseRepository; +import nextstep.courses.domain.repository.RegistrationRepository; +import nextstep.courses.domain.repository.SessionCoverRepository; +import nextstep.courses.domain.repository.SessionRepository; +import nextstep.users.domain.NsUser; +import nextstep.users.domain.UserRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +public class SessionService { + private final CourseRepository courseRepository; + private final RegistrationRepository registrationRepository; + private final SessionRepository sessionRepository; + private final UserRepository userRepository; + private final SessionCoverRepository sessionCoverRepository; + + public SessionService(CourseRepository courseRepository, RegistrationRepository registrationRepository, SessionRepository sessionRepository, UserRepository userRepository, SessionCoverRepository sessionCoverRepository) { + this.courseRepository = courseRepository; + this.registrationRepository = registrationRepository; + this.sessionRepository = sessionRepository; + this.userRepository = userRepository; + this.sessionCoverRepository = sessionCoverRepository; + } + + @Transactional + public void registerSession(String userId, Long sessionId, Long amount) { + NsUser user = userRepository.findByUserId(userId).orElseThrow(() -> new IllegalArgumentException("유효한 userId가 아닙니다.")); + Session session = retrieveSession(sessionId); + + registrationRepository.save(Registration.register(user, session, amount)); + } + + private Session retrieveSession(Long sessionId) { + Session session = sessionRepository.findById(sessionId).orElseThrow(() -> new IllegalArgumentException("유효한 sessionId가 아닙니다.")); + + Course course = courseRepository.findById(session.course().id()); + + SessionCover sessionCover = sessionCoverRepository.findById(session.sessionCover().id()); + + List<Registration> registrations = registrationRepository.findAllBySessionId(sessionId); + List<NsUser> users = userRepository.findAllByIds(registrations.stream().map(r -> r.nsUser().getId()).collect(Collectors.toList())); + + return Session.fromSession(session, sessionCover, course, users); + } + + +} diff --git a/src/main/java/nextstep/courses/utils/BaseEntity.java b/src/main/java/nextstep/courses/utils/BaseEntity.java new file mode 100644 index 000000000..56420fad1 --- /dev/null +++ b/src/main/java/nextstep/courses/utils/BaseEntity.java @@ -0,0 +1,17 @@ +package nextstep.courses.utils; + +import java.time.LocalDateTime; + +public class BaseEntity { + public LocalDateTime createdAt; + + public LocalDateTime updatedAt; + + public LocalDateTime createdAt() { + return createdAt; + } + + public LocalDateTime updatedAt() { + return updatedAt; + } +} diff --git a/src/main/java/nextstep/courses/utils/DateUtil.java b/src/main/java/nextstep/courses/utils/DateUtil.java new file mode 100644 index 000000000..0d5b91174 --- /dev/null +++ b/src/main/java/nextstep/courses/utils/DateUtil.java @@ -0,0 +1,13 @@ +package nextstep.courses.utils; + +import java.sql.Timestamp; +import java.time.LocalDateTime; + +public class DateUtil { + public static LocalDateTime toLocalDateTime(Timestamp timestamp) { + if (timestamp == null) { + return null; + } + return timestamp.toLocalDateTime(); + } +} diff --git a/src/main/java/nextstep/courses/utils/RasterProcessor.java b/src/main/java/nextstep/courses/utils/RasterProcessor.java index 7462317cb..4373d6fa2 100644 --- a/src/main/java/nextstep/courses/utils/RasterProcessor.java +++ b/src/main/java/nextstep/courses/utils/RasterProcessor.java @@ -18,7 +18,7 @@ public SessionCover processImage(File file) throws IOException { throw new FileNotFoundException("Invalid image file."); } - return new SessionCover(image.getWidth(), image.getHeight(), file.length(), writeRasterImage(image)); + return new SessionCover(2L, image.getWidth(), image.getHeight(), file.length(), writeRasterImage(image)); } private static byte[] writeRasterImage(BufferedImage image) throws IOException { diff --git a/src/main/java/nextstep/courses/utils/VectorProcessor.java b/src/main/java/nextstep/courses/utils/VectorProcessor.java index 855b896ff..a447b4e0a 100644 --- a/src/main/java/nextstep/courses/utils/VectorProcessor.java +++ b/src/main/java/nextstep/courses/utils/VectorProcessor.java @@ -17,7 +17,7 @@ public class VectorProcessor implements ImageProcessor { @Override public SessionCover processImage(File file) throws IOException { Element svgRoot = parseXML(file); - return new SessionCover(parseInt(svgRoot.getAttribute("width")), parseInt(svgRoot.getAttribute("height")), file.length(), writeVectorImage(file)); + return new SessionCover(1L, parseInt(svgRoot.getAttribute("width")), parseInt(svgRoot.getAttribute("height")), file.length(), writeVectorImage(file)); } private int parseInt(String pixel) { diff --git a/src/main/java/nextstep/payments/service/PaymentService.java b/src/main/java/nextstep/payments/service/PaymentService.java index 7bfc4d357..e43203c2d 100644 --- a/src/main/java/nextstep/payments/service/PaymentService.java +++ b/src/main/java/nextstep/payments/service/PaymentService.java @@ -1,14 +1,14 @@ package nextstep.payments.service; -import nextstep.courses.domain.Registration; import nextstep.courses.domain.Session; import nextstep.payments.domain.Payment; import nextstep.users.domain.NsUser; +import org.springframework.stereotype.Service; +@Service public class PaymentService { + public Payment payment(NsUser user, Session session, Long amount) { - Registration registration = new Registration(); - registration.register(user, session, amount); return new Payment(session.id()); } } diff --git a/src/main/java/nextstep/users/domain/NsUser.java b/src/main/java/nextstep/users/domain/NsUser.java index 62ec5138c..01ab7fc85 100755 --- a/src/main/java/nextstep/users/domain/NsUser.java +++ b/src/main/java/nextstep/users/domain/NsUser.java @@ -25,6 +25,10 @@ public class NsUser { public NsUser() { } + public NsUser(Long id) { + this.id = id; + } + public NsUser(Long id, String userId, String password, String name, String email) { this(id, userId, password, name, email, LocalDateTime.now(), null); } diff --git a/src/main/java/nextstep/users/domain/UserRepository.java b/src/main/java/nextstep/users/domain/UserRepository.java index 745a85dc1..b0618c3b7 100755 --- a/src/main/java/nextstep/users/domain/UserRepository.java +++ b/src/main/java/nextstep/users/domain/UserRepository.java @@ -1,7 +1,10 @@ package nextstep.users.domain; +import java.util.List; import java.util.Optional; public interface UserRepository { Optional<NsUser> findByUserId(String userId); + + List<NsUser> findAllByIds(List<Long> ids); } diff --git a/src/main/java/nextstep/users/infrastructure/JdbcUserRepository.java b/src/main/java/nextstep/users/infrastructure/JdbcUserRepository.java index 005f04fc5..2c42cb12e 100644 --- a/src/main/java/nextstep/users/infrastructure/JdbcUserRepository.java +++ b/src/main/java/nextstep/users/infrastructure/JdbcUserRepository.java @@ -4,10 +4,14 @@ import nextstep.users.domain.UserRepository; import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Repository; import java.sql.Timestamp; import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Optional; @Repository("userRepository") @@ -32,6 +36,27 @@ public Optional<NsUser> findByUserId(String userId) { return Optional.of(jdbcTemplate.queryForObject(sql, rowMapper, userId)); } + @Override + public List<NsUser> findAllByIds(List<Long> ids) { + NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate); + + String sql = "SELECT id, user_id, password, name, email, created_at, updated_at FROM ns_user WHERE id IN (:ids)"; + + Map<String, Object> params = new HashMap<>(); + params.put("ids", ids); + + RowMapper<NsUser> rowMapper = (rs, rowNum) -> new NsUser( + rs.getLong("id"), + rs.getString("user_id"), + rs.getString("password"), + rs.getString("name"), + rs.getString("email"), + toLocalDateTime(rs.getTimestamp("created_at")), + toLocalDateTime(rs.getTimestamp("updated_at"))); + + return namedParameterJdbcTemplate.query(sql, params, rowMapper); + } + private LocalDateTime toLocalDateTime(Timestamp timestamp) { if (timestamp == null) { return null; diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 8d5a988c8..9763759b4 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -1,50 +1,100 @@ -create table course ( - id bigint generated by default as identity, - title varchar(255) not null, - creator_id bigint not null, - created_at timestamp not null, +create table course +( + id bigint generated by default as identity, + title varchar(255) not null, + creator_id bigint not null, + created_at timestamp not null, updated_at timestamp, primary key (id) ); -create table ns_user ( - id bigint generated by default as identity, - user_id varchar(20) not null, - password varchar(20) not null, - name varchar(20) not null, - email varchar(50), +create table semester +( + id bigint generated by default as identity, + course_id bigint not null, created_at timestamp not null, updated_at timestamp, primary key (id) ); -create table question ( - id bigint generated by default as identity, - created_at timestamp not null, +create table session +( + id bigint generated by default as identity, + begin_dt timestamp, + end_dt timestamp, + session_status varchar(10), + capacity bigint, + price bigint, + course_id bigint not null, + session_cover_id bigint, + created_at timestamp, + updated_at timestamp, + primary key (id) +); + +create table registration +( + id bigint generated by default as identity, + ns_user_id bigint not null, + session_id bigint not null, + amount bigint, + created_at timestamp, updated_at timestamp, - contents clob, - deleted boolean not null, - title varchar(100) not null, - writer_id bigint, primary key (id) ); -create table answer ( - id bigint generated by default as identity, - created_at timestamp not null, +create table session_cover +( + id bigint generated by default as identity, + image blob, + created_at timestamp, + updated_at timestamp, + primary key (id) +); + + +create table ns_user +( + id bigint generated by default as identity, + user_id varchar(20) not null, + password varchar(20) not null, + name varchar(20) not null, + email varchar(50), + created_at timestamp not null, updated_at timestamp, - contents clob, - deleted boolean not null, + primary key (id) +); + +create table question +( + id bigint generated by default as identity, + created_at timestamp not null, + updated_at timestamp, + contents clob, + deleted boolean not null, + title varchar(100) not null, + writer_id bigint, + primary key (id) +); + +create table answer +( + id bigint generated by default as identity, + created_at timestamp not null, + updated_at timestamp, + contents clob, + deleted boolean not null, question_id bigint, - writer_id bigint, + writer_id bigint, primary key (id) ); -create table delete_history ( - id bigint not null, - content_id bigint, - content_type varchar(255), - created_date timestamp, +create table delete_history +( + id bigint not null, + content_id bigint, + content_type varchar(255), + created_date timestamp, deleted_by_id bigint, primary key (id) ); diff --git a/src/test/java/nextstep/courses/domain/SessionTest.java b/src/test/java/nextstep/courses/domain/SessionTest.java index c3ad572ca..6a36f4d25 100644 --- a/src/test/java/nextstep/courses/domain/SessionTest.java +++ b/src/test/java/nextstep/courses/domain/SessionTest.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test; import java.time.LocalDateTime; +import java.util.ArrayList; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; @@ -13,8 +14,8 @@ class SessionTest { @Test void 무료강의생성() { - Session freeSession = new FreeSession(1L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1), new SessionCover(300, 200, 1024, null), new Course()); - assertThat(freeSession.id).isEqualTo(1L); + Session freeSession = Session.ofFree(1L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1), new SessionCover(3L, 300, 200, 1024, new byte[100]), new Course(), new ArrayList<>()); + assertThat(freeSession.id()).isEqualTo(1L); } @Test @@ -22,14 +23,14 @@ class SessionTest { long sessionPrice = 1_000_000L; int sessionCapacity = 100; - Session paidSession = new PaidSession(2L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1), - new SessionCover(300, 200, 1024, null), new Course(), sessionCapacity, sessionPrice); - assertThat(paidSession.id).isEqualTo(2L); + Session paidSession = Session.ofPaid(2L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1), + new SessionCover(4L, 300, 200, 1024, new byte[100]), new Course(), sessionPrice, sessionCapacity, new ArrayList<>()); + assertThat(paidSession.id()).isEqualTo(2L); } @Test void 수강신청상태_exception() { - Session session = new FreeSession(5L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1), new SessionCover(300, 200, 1024, null), new Course()); + Session session = Session.ofFree(5L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1), new SessionCover(5L, 300, 200, 1024, new byte[100]), new Course(), new ArrayList<>()); assertThatExceptionOfType(BusinessInvalidValueException.class) .isThrownBy(() -> session.enroll(new NsUser(), 0L)) .withMessage("수강신청 가능한 상태가 아닙니다."); @@ -37,8 +38,8 @@ class SessionTest { @Test void 최대수강인원_exception() { - Session paidSession = new PaidSession(3L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1), - new SessionCover(300, 200, 1024, null), new Course(), 0, 1_000_000L); + Session paidSession = Session.ofPaid(2L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1), + new SessionCover(6L, 300, 200, 1024, new byte[100]), new Course(), 10000L, 0, new ArrayList<>()); paidSession.startEnrollment(); assertThatExceptionOfType(BusinessInvalidValueException.class) @@ -48,8 +49,8 @@ class SessionTest { @Test void 가격비교_exception() { - Session paidSession = new PaidSession(4L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1), - new SessionCover(300, 200, 1024, null), new Course(), 10, 1_000_000L); + Session paidSession = Session.ofPaid(2L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1), + new SessionCover(7L, 300, 200, 1024, new byte[100]), new Course(), 10000L, 1, new ArrayList<>()); paidSession.startEnrollment(); assertThatExceptionOfType(BusinessInvalidValueException.class) diff --git a/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java index f087fc0ad..5b7987c4f 100644 --- a/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java +++ b/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java @@ -1,7 +1,7 @@ package nextstep.courses.infrastructure; import nextstep.courses.domain.Course; -import nextstep.courses.domain.CourseRepository; +import nextstep.courses.domain.repository.CourseRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; diff --git a/src/test/java/nextstep/courses/infrastructure/RegistrationRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/RegistrationRepositoryTest.java new file mode 100644 index 000000000..9fdd6198a --- /dev/null +++ b/src/test/java/nextstep/courses/infrastructure/RegistrationRepositoryTest.java @@ -0,0 +1,33 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.Course; +import nextstep.courses.domain.Registration; +import nextstep.courses.domain.Session; +import nextstep.courses.domain.SessionCover; +import nextstep.courses.domain.repository.RegistrationRepository; +import nextstep.users.domain.NsUser; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + + +@SpringBootTest +class RegistrationRepositoryTest { + @Autowired + private RegistrationRepository registrationRepository; + + @Test + void crud() { + int id = registrationRepository.save(new Registration(new NsUser(1L, "1", "2", "3", "4") + , Session.ofFree(1L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1), new SessionCover(1L, 300, 200, 1024, new byte[100]), new Course(), new ArrayList<>()) + , 100000L)); + List<Registration> registrations = registrationRepository.findAllBySessionId(1L); + assertThat(registrations.get(0).nsUser().getId()).isEqualTo(1L); + } +} \ No newline at end of file diff --git a/src/test/java/nextstep/courses/infrastructure/SessionCoverRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/SessionCoverRepositoryTest.java new file mode 100644 index 000000000..f0c0b1bed --- /dev/null +++ b/src/test/java/nextstep/courses/infrastructure/SessionCoverRepositoryTest.java @@ -0,0 +1,22 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.SessionCover; +import nextstep.courses.domain.repository.SessionCoverRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@SpringBootTest +class SessionCoverRepositoryTest { + @Autowired + private SessionCoverRepository sessionCoverRepository; + + @Test + void crud() { + int id = sessionCoverRepository.save(new SessionCover(new byte[100])); + SessionCover sessionCover = sessionCoverRepository.findById((long) id); + assertThat(sessionCover.id()).isEqualTo(id); + } +} \ No newline at end of file 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 000000000..dc357724e --- /dev/null +++ b/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java @@ -0,0 +1,28 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.Course; +import nextstep.courses.domain.Session; +import nextstep.courses.domain.SessionCover; +import nextstep.courses.domain.repository.SessionRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.time.LocalDateTime; +import java.util.ArrayList; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@SpringBootTest +class SessionRepositoryTest { + + @Autowired + private SessionRepository sessionRepository; + + @Test + void crud() { + int id = sessionRepository.save(Session.ofFree(3L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1), new SessionCover(100L, 300, 200, 1024, new byte[100]), new Course(100L, "제목", 3L), new ArrayList<>())); + Session session = sessionRepository.findById((long) id).orElseThrow(() -> new IllegalArgumentException("유효하지 않은 id입니다.")); + assertThat(session.id()).isEqualTo(id); + } +} \ No newline at end of file