Skip to content

Step4 #772

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: cm0x0x0x0
Choose a base branch
from
Open

Step4 #772

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,9 @@
- boolean validateFileSize()
- boolean validateFileType()
- boolean validateRatio()



## 🚀 4단계 - 수강신청(요구사항 변경)
- [x] SessionStatus 진행상태와 모집상태로 분리
- [x] coverImage 여러개 가능하도록 기능 추가
- [x] 수강 승인 및 수강 취소 기능 추가
43 changes: 43 additions & 0 deletions src/main/java/nextstep/courses/domain/Enrollment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package nextstep.courses.domain;

public class Enrollment {
private Member student;
private Session session;
private EnrollmentStatus status; // PENDING, APPROVED, REJECTED

public Enrollment(Member student, Session session) {
this.student = student;
this.session = session;
this.status = EnrollmentStatus.PENDING;
}

public void approve() {
if (this.status == EnrollmentStatus.APPROVED) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • this.status.isApproved() 로 메시지를 보내보는건 어떨까요? 그리고 이미 작성한 isApproved() 메서드를 활용해보는건 어떨까요?

throw new IllegalStateException("이미 승인된 수강 신청입니다.");
}

this.status = EnrollmentStatus.APPROVED;
session.accept();
}

public void reject() {
this.status = EnrollmentStatus.REJECTED;
}

public boolean isApproved() {
return status == EnrollmentStatus.APPROVED;
}

public Member getStudent() {
return student;
}

public EnrollmentStatus getStatus() {
return status;
}

public Session getSession() {
return session;
}
}

5 changes: 5 additions & 0 deletions src/main/java/nextstep/courses/domain/EnrollmentStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package nextstep.courses.domain;

public enum EnrollmentStatus {
PENDING, APPROVED, REJECTED;
}
Comment on lines +3 to +5

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 강의 진행 상태(준비중, 진행중, 종료)와 모집 상태(비모집중, 모집중)로 상태 값을 분리 입니다.

29 changes: 29 additions & 0 deletions src/main/java/nextstep/courses/domain/Enrollments.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package nextstep.courses.domain;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Enrollments {
private final List<Enrollment> values = new ArrayList<>();

public void addEnrollment(Enrollment enrollment) {
values.add(enrollment);
}

public boolean isEnrolledBy(Member student) {
return values.stream()
.anyMatch(e -> e.getStudent().equals(student));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 값을 꺼내서 비교한다면, 늘 리팩토링 여지가 있지는 않은지 고민해보세요. 여기도 메시지를 보내볼 수 있겠죠??

}

public Enrollment findByMember(Member student) {
return values.stream()
.filter(e -> e.getStudent().equals(student))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("해당 회원의 수강 신청이 존재하지 않습니다."));
}

public List<Enrollment> getValues() {
return Collections.unmodifiableList(values);
}
}
8 changes: 8 additions & 0 deletions src/main/java/nextstep/courses/domain/Image.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,18 @@

public class Image {
private static final Set<String> ALLOWED_FILE_FORMAT = new HashSet<>(List.of("gif", "jpg", "jpeg", "png", "svg"));
private Long id;
private final File file;
private String imageUrl;
private int width;
private int height;

// DB용 생성자 (id 포함, 검증 생략 가능)
public Image(Long id, float fileSize, String fileType, String imageUrl, int width, int height) {
this(fileSize, fileType, imageUrl, width, height);
this.id = id;
}

public Image(float fileSize, String fileType, String imageUrl, int width, int height) {
this.file = new File(ALLOWED_FILE_FORMAT, fileSize, fileType);
this.imageUrl = imageUrl;
Expand All @@ -19,6 +26,7 @@ public Image(float fileSize, String fileType, String imageUrl, int width, int he
validate();
}


private void validate() {
validateFileSize();
validateFileType();
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/nextstep/courses/domain/Images.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package nextstep.courses.domain;

import java.util.Collections;
import java.util.List;

public class Images {
private List<Image> images;

public Images(List<Image> images) {
this.images = List.copyOf(images);
}

public List<Image> getImages() {
return Collections.unmodifiableList(images);
}
}
Comment on lines +6 to +16

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 데이터 홀더 외는 별 다른 역할이 없는걸까요?

41 changes: 41 additions & 0 deletions src/main/java/nextstep/courses/domain/Member.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package nextstep.courses.domain;

import java.util.Objects;

public class Member {
private final Long id;
private final String name;
private final String email;

public Member(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}

public Long getId() {
return id;
}

public String getName() {
return name;
}

public String getEmail() {
return email;
}

// Member 식별 비교 (예: Set 사용 시 필요)
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Member)) return false;
Member member = (Member) o;
return Objects.equals(id, member.id);
}

@Override
public int hashCode() {
return Objects.hash(id);
}
}
6 changes: 6 additions & 0 deletions src/main/java/nextstep/courses/domain/RecruitmentStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package nextstep.courses.domain;

public enum RecruitmentStatus {
NOT_RECRUITING,
RECRUITING
}
54 changes: 43 additions & 11 deletions src/main/java/nextstep/courses/domain/Session.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,33 @@
import nextstep.payments.domain.Payment;

import java.time.LocalDateTime;
import java.util.regex.Pattern;

public class Session {
private CapacityInfo capacityInfo;
private SessionPeriod sessionPeriod;
private String title;
private int id;
private Long tuition;
private Image coverImage;
private Images coverImages;
private RecruitmentStatus recruitmentStatus;
private SessionStatus sessionStatus;
private JoinStrategy joinStrategy;
private final Enrollments enrollments = new Enrollments();


public Session(String title, int id, LocalDateTime startDate, LocalDateTime endDate, Long tuition, int currentCount, int capacity, Image coverImage, SessionStatus sessionStatus) {
this(title, id, startDate, endDate, tuition, currentCount, capacity, coverImage, sessionStatus, tuition == 0 ? new FreeJoinStrategy() : new PaidJoinStrategy());
public Session(String title, int id, LocalDateTime startDate, LocalDateTime endDate, Long tuition, int currentCount, int capacity, Images coverImages, SessionStatus sessionStatus, RecruitmentStatus recruitmentStatus) {
this(title, id, startDate, endDate, tuition, currentCount, capacity, coverImages, sessionStatus, recruitmentStatus, tuition == 0 ? new FreeJoinStrategy() : new PaidJoinStrategy());
}

public Session(String title, int id, LocalDateTime startDate, LocalDateTime endDate, Long tuition, int currentCount, int capacity, Image coverImage, SessionStatus sessionStatus, JoinStrategy joinStrategy) {
public Session(String title, int id, LocalDateTime startDate, LocalDateTime endDate, Long tuition, int currentCount, int capacity, Images coverImages, SessionStatus sessionStatus, RecruitmentStatus recruitmentStatus, JoinStrategy joinStrategy) {
this.title = title;
this.id = id;
this.sessionPeriod = new SessionPeriod(startDate, endDate);
this.tuition = tuition;
this.capacityInfo = new CapacityInfo(currentCount, capacity);
this.coverImage = coverImage;
this.coverImages = coverImages;
this.sessionStatus = sessionStatus;
this.recruitmentStatus = recruitmentStatus;
this.joinStrategy = joinStrategy;
}

Expand All @@ -36,7 +38,7 @@ boolean joinable(Payment pay) {
}

public boolean recruiting() {
return sessionStatus == SessionStatus.RECRUITING;
return sessionStatus == SessionStatus.ONGOING && recruitmentStatus == RecruitmentStatus.RECRUITING;
}

public boolean underCapacity() {
Expand All @@ -51,14 +53,32 @@ public boolean hasId(long id) {
return this.id == id;
}

public void enroll(Payment pay) {
public void enroll(Payment pay, Member member) {
if (!joinable(pay)) {
throw new IllegalStateException("수강 신청 조건을 만족하지 않습니다.");
}

if (enrollments.isEnrolledBy(member)) {
throw new IllegalStateException("이미 수강 신청한 회원입니다.");
}

enrollments.addEnrollment(new Enrollment(member, this));
}

public void approveEnrollment(Member member) {
Enrollment enrollment = enrollments.findByMember(member);
enrollment.approve();
}

public void accept(){
this.capacityInfo.increaseCurrentCount();
}

public void rejectEnrollment(Member member) {
Enrollment enrollment = enrollments.findByMember(member);
enrollment.reject();
}

public String getTitle() {
return title;
}
Expand Down Expand Up @@ -87,11 +107,23 @@ public int getCapacity() {
return capacityInfo.getCapacity();
}

public Image getCoverImage() {
return coverImage;
public Images getCoverImages() {
return coverImages;
}

public Image getMainCoverImage() {
return coverImages.getImages().get(0);
}

public SessionStatus getStatus() {
public SessionStatus getSessionStatus() {
return sessionStatus;
}

public RecruitmentStatus getRecruitmentStatus() {
return recruitmentStatus;
}

public Enrollments getEnrollments() {
return enrollments;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

public interface SessionRepository {
int save(Session session, Long courseId);

void saveImage(int sessionId, Image image);
Session findById(Long id);
}
2 changes: 1 addition & 1 deletion src/main/java/nextstep/courses/domain/SessionStatus.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

public enum SessionStatus {
PREPARING,
RECRUITING,
ONGOING,
CLOSED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package nextstep.courses.infrastructure;

import nextstep.courses.domain.Enrollment;
import nextstep.courses.domain.EnrollmentStatus;
import nextstep.courses.domain.Member;
import nextstep.courses.domain.Session;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public class JdbcEnrollmentRepository {

private final JdbcOperations jdbc;
private final JdbcSessionRepository jdbcSessionRepository;

public JdbcEnrollmentRepository(JdbcOperations jdbc, JdbcSessionRepository jdbcSessionRepository) {
this.jdbc = jdbc;
this.jdbcSessionRepository = jdbcSessionRepository;
}

public void save(Long sessionId, Long memberId, EnrollmentStatus status) {
String sql = "INSERT INTO enrollment (session_id, member_id, status) VALUES (?, ?, ?)";
jdbc.update(sql, sessionId, memberId, status.name());
}

public void updateStatus(Long sessionId, Long memberId, EnrollmentStatus status) {
String sql = "UPDATE enrollment SET status = ? WHERE session_id = ? AND member_id = ?";
jdbc.update(sql, status.name(), sessionId, memberId);
}

public List<Enrollment> findBySessionId(Long sessionId) {
String sql =
"SELECT e.*, m.id as member_id, m.name, m.email " +
"FROM enrollment e " +
"JOIN member m ON e.member_id = m.id " +
"WHERE e.session_id = ?";


return jdbc.query(sql, (rs, rowNum) -> {
Member member = new Member(
rs.getLong("member_id"),
rs.getString("name"),
rs.getString("email")
);

Session session = jdbcSessionRepository.findById(sessionId);
Enrollment enrollment = new Enrollment(member, session);
EnrollmentStatus status = EnrollmentStatus.valueOf(rs.getString("status"));
if (status == EnrollmentStatus.APPROVED) {
enrollment.approve();
} else if (status == EnrollmentStatus.REJECTED) {
enrollment.reject();
}
Comment on lines +51 to +55

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 도메인 정책이 인프라 레이어에까지 침투했네요


return enrollment;
}, sessionId);
}
}
Loading