Skip to content

Commit

Permalink
feat: Study V2 도메인 구현 (#853)
Browse files Browse the repository at this point in the history
* docs: 투두 추가

* feat: 학기 VO 추가

* feat: 스터디 V2 엔티티 구현

* feat: 디스코드 관련 필드 추가

* feat: 스터디회차 컬렉션 필드 추가

* feat: 스터디회차 엔티티 구현

* feat: 영속성 전이 및 고아객체 제거 옵션 활성화

* fix: `@Embedded` 로 수정

* refactor: totalRound로 이름 변경

* fix: totalRound 미반영 파라미터 수정

* fix: static 키워드 추가
  • Loading branch information
uwoobeat authored Jan 28, 2025
1 parent d2df1e2 commit 3f5ea6e
Show file tree
Hide file tree
Showing 4 changed files with 287 additions and 0 deletions.
22 changes: 22 additions & 0 deletions src/main/java/com/gdschongik/gdsc/domain/common/vo/Semester.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.gdschongik.gdsc.domain.common.vo;

import com.gdschongik.gdsc.domain.common.model.SemesterType;
import jakarta.persistence.Embeddable;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Embeddable
@EqualsAndHashCode
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Semester {

private Integer academicYear;

@Enumerated(EnumType.STRING)
private SemesterType semesterType;
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ private void validateDateTimeWithinTwoWeeks(LocalDateTime dateTime, LocalDateTim

private void validatePeriodOverlap(
List<RecruitmentRound> recruitmentRounds, LocalDateTime startDate, LocalDateTime endDate) {
// TODO: recruitmentRound.getPeriod().validatePeriodOverlap(startDate, endDate)로 변경
recruitmentRounds.forEach(recruitmentRound -> recruitmentRound.validatePeriodOverlap(startDate, endDate));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package com.gdschongik.gdsc.domain.studyv2.domain;

import com.gdschongik.gdsc.domain.common.model.BaseEntity;
import com.gdschongik.gdsc.domain.common.vo.Period;
import jakarta.persistence.AttributeOverride;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.hibernate.annotations.Comment;

@Getter
@Entity
@Table(name = "study_session_v2")
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
public class StudySessionV2 extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "study_session_v2_id")
private Long id;

@Comment("회차 제목")
private String title;

@Comment("회차 설명")
private String description;

// 수업 관련 필드

@Comment("수업 출석 번호")
private Integer lessonAttendanceNumber;

@Embedded
@AttributeOverride(name = "startDate", column = @Column(name = "lesson_start_at"))
@AttributeOverride(name = "endDate", column = @Column(name = "lesson_end_at"))
private Period lessonPeriod;

// 과제 관련 필드

@Comment("과제 명세 링크")
private String assignmentDescriptionLink;

@Embedded
@AttributeOverride(name = "startDate", column = @Column(name = "assignment_start_at"))
@AttributeOverride(name = "endDate", column = @Column(name = "assignment_end_at"))
private Period assignmentPeriod;

// 참조 필드

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "study_v2_id")
private StudyV2 studyV2;

/**
* 모든 스터디회차는 인자로 전달되는 스터디 애그리거트 루트 엔티티에 종속됩니다.
* 따라서 스터디회차 생성 팩토리 메서드 생성 시 자기 자신을 반환하지 않고,
* 스터디 애그리거트 루트 엔티티의 스터디회차 컬렉션에 자기 자신을 추가하는 식으로 생성합니다.
* 부모 클래스에서 위 로직을 수행하기 때문에, 생성 팩토리 메서드는 void 타입을 반환해야 합니다.
*/
@Builder(access = AccessLevel.PRIVATE)
private StudySessionV2(
String title,
String description,
Integer lessonAttendanceNumber,
Period lessonPeriod,
String assignmentDescriptionLink,
Period assignmentPeriod,
StudyV2 studyV2) {
this.title = title;
this.description = description;
this.lessonAttendanceNumber = lessonAttendanceNumber;
this.lessonPeriod = lessonPeriod;
this.assignmentDescriptionLink = assignmentDescriptionLink;
this.assignmentPeriod = assignmentPeriod;
this.studyV2 = studyV2;
studyV2.getStudySessions().add(this);
}

public static void create(
String title,
String description,
Integer lessonAttendanceNumber,
Period lessonPeriod,
String assignmentDescriptionLink,
Period assignmentPeriod,
StudyV2 studyV2) {
StudySessionV2.builder()
.title(title)
.description(description)
.lessonAttendanceNumber(lessonAttendanceNumber)
.lessonPeriod(lessonPeriod)
.assignmentDescriptionLink(assignmentDescriptionLink)
.assignmentPeriod(assignmentPeriod)
.studyV2(studyV2)
.build();
}
}
156 changes: 156 additions & 0 deletions src/main/java/com/gdschongik/gdsc/domain/studyv2/domain/StudyV2.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package com.gdschongik.gdsc.domain.studyv2.domain;

import com.gdschongik.gdsc.domain.common.model.BaseEntity;
import com.gdschongik.gdsc.domain.common.vo.Period;
import com.gdschongik.gdsc.domain.common.vo.Semester;
import com.gdschongik.gdsc.domain.member.domain.Member;
import com.gdschongik.gdsc.domain.study.domain.StudyType;
import jakarta.persistence.AttributeOverride;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.time.DayOfWeek;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Comment;

/**
* 스터디 애그리거트 루트 엔티티입니다.
* [메타] 태그가 있는 필드는 수강 신청 시 참고용으로만 사용되며, 스터디회차 엔티티 내부 상태 검증에 사용되지 않습니다.
* 혼동이 예상되는 경우 코멘트에 해당 태그가 명시되어 있습니다.
* 그 외 필드의 경우 스터디 애그리거트 내부 상태 검증에 사용될 수 있습니다. (ex: 총 회차 수)
*/
@Getter
@Entity
@Table(name = "study_v2")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class StudyV2 extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "study_v2_id")
private Long id;

private StudyType type;

@Comment("스터디 이름")
private String title;

@Comment("스터디 소개")
private String description;

@Comment("스터디 소개 노션 링크")
@Column(columnDefinition = "TEXT")
private String descriptionNotionLink;

@Embedded
private Semester semester;

@Comment("총 회차 수")
private Integer totalRound;

@Comment("[메타] 스터디 요일")
@Enumerated(EnumType.STRING)
private DayOfWeek dayOfWeek;

@Comment("[메타] 스터디 시작 시간")
private LocalTime startTime;

@Comment("[메타] 스터디 종료 시간")
private LocalTime endTime;

@Embedded
@AttributeOverride(name = "startDate", column = @Column(name = "application_start_date"))
@AttributeOverride(name = "endDate", column = @Column(name = "application_end_date"))
private Period applicationPeriod;

@Comment("디스코드 채널 ID")
private String discordChannelId;

@Comment("디스코드 역할 ID")
private String discordRoleId;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member mentor;

@OneToMany(mappedBy = "studyV2", cascade = CascadeType.ALL, orphanRemoval = true)
private List<StudySessionV2> studySessions = new ArrayList<>();

@Builder(access = AccessLevel.PRIVATE)
private StudyV2(
StudyType type,
String title,
String description,
String descriptionNotionLink,
Semester semester,
Integer totalRound,
DayOfWeek dayOfWeek,
LocalTime startTime,
LocalTime endTime,
Period applicationPeriod,
String discordChannelId,
String discordRoleId,
Member mentor) {
this.type = type;
this.title = title;
this.description = description;
this.descriptionNotionLink = descriptionNotionLink;
this.semester = semester;
this.totalRound = totalRound;
this.dayOfWeek = dayOfWeek;
this.startTime = startTime;
this.endTime = endTime;
this.applicationPeriod = applicationPeriod;
this.discordChannelId = discordChannelId;
this.discordRoleId = discordRoleId;
this.mentor = mentor;
}

public static StudyV2 create(
StudyType type,
String title,
String description,
String descriptionNotionLink,
Semester semester,
Integer totalRound,
DayOfWeek dayOfWeek,
LocalTime startTime,
LocalTime endTime,
Period applicationPeriod,
String discordChannelId,
String discordRoleId,
Member mentor) {
return StudyV2.builder()
.type(type)
.title(title)
.description(description)
.descriptionNotionLink(descriptionNotionLink)
.semester(semester)
.totalRound(totalRound)
.dayOfWeek(dayOfWeek)
.startTime(startTime)
.endTime(endTime)
.applicationPeriod(applicationPeriod)
.discordChannelId(discordChannelId)
.discordRoleId(discordRoleId)
.mentor(mentor)
.build();
}
}

0 comments on commit 3f5ea6e

Please sign in to comment.