Skip to content
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

feat: 리쿠르팅에 회비와 차수 추가 #383

Merged
merged 5 commits into from
Jun 14, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import static com.gdschongik.gdsc.global.exception.ErrorCode.*;

import com.gdschongik.gdsc.domain.common.model.SemesterType;
import com.gdschongik.gdsc.domain.common.vo.Money;
import com.gdschongik.gdsc.domain.recruitment.dao.RecruitmentRepository;
import com.gdschongik.gdsc.domain.recruitment.domain.Recruitment;
import com.gdschongik.gdsc.domain.recruitment.domain.RoundType;
import com.gdschongik.gdsc.domain.recruitment.dto.request.RecruitmentCreateRequest;
import com.gdschongik.gdsc.global.exception.CustomException;
import java.time.LocalDateTime;
Expand All @@ -30,11 +32,17 @@ public void createRecruitment(RecruitmentCreateRequest request) {
validatePeriodWithinTwoWeeks(
request.startDate(), request.endDate(), request.academicYear(), request.semesterType());
validatePeriodOverlap(request.academicYear(), request.semesterType(), request.startDate(), request.endDate());
validateRoundOverlap(request.academicYear(), request.semesterType(), request.roundType());

Recruitment recruitment = Recruitment.createRecruitment(
request.name(), request.startDate(), request.endDate(), request.academicYear(), request.semesterType());
request.name(),
request.startDate(),
request.endDate(),
request.academicYear(),
request.semesterType(),
request.roundType(),
Money.from(request.fee()));
recruitmentRepository.save(recruitment);
// todo: recruitment 모집 시작 직전에 멤버 역할 수정하는 로직 필요.
}

private void validatePeriodMatchesAcademicYear(
Expand Down Expand Up @@ -105,4 +113,11 @@ private void validatePeriodOverlap(

recruitments.forEach(recruitment -> recruitment.validatePeriodOverlap(startDate, endDate));
}

private void validateRoundOverlap(Integer academicYear, SemesterType semesterType, RoundType roundType) {
if (recruitmentRepository.existsByAcademicYearAndSemesterTypeAndRoundType(
academicYear, semesterType, roundType)) {
throw new CustomException(RECRUITMENT_ROUND_TYPE_OVERLAP);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

import com.gdschongik.gdsc.domain.common.model.SemesterType;
import com.gdschongik.gdsc.domain.recruitment.domain.Recruitment;
import com.gdschongik.gdsc.domain.recruitment.domain.RoundType;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;

public interface RecruitmentRepository extends JpaRepository<Recruitment, Long>, RecruitmentCustomRepository {

List<Recruitment> findAllByAcademicYearAndSemesterType(Integer academicYear, SemesterType semesterType);

boolean existsByAcademicYearAndSemesterTypeAndRoundType(
Integer academicYear, SemesterType semesterType, RoundType roundType);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.gdschongik.gdsc.domain.common.model.BaseSemesterEntity;
import com.gdschongik.gdsc.domain.common.model.SemesterType;
import com.gdschongik.gdsc.domain.common.vo.Money;
import com.gdschongik.gdsc.domain.recruitment.domain.vo.Period;
import jakarta.persistence.*;
import java.time.LocalDateTime;
Expand All @@ -25,25 +26,43 @@ public class Recruitment extends BaseSemesterEntity {
@Embedded
private Period period;

@Embedded
private Money fee;
Copy link
Member

Choose a reason for hiding this comment

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

VO라서 @Embedded 붙여야 하지 않나요?


@Enumerated(EnumType.STRING)
private RoundType roundType;

@Builder(access = AccessLevel.PRIVATE)
private Recruitment(String name, final Period period, Integer academicYear, SemesterType semesterType) {
private Recruitment(
String name,
final Period period,
Integer academicYear,
SemesterType semesterType,
Money fee,
RoundType roundType) {
super(academicYear, semesterType);
this.name = name;
this.period = period;
this.fee = fee;
this.roundType = roundType;
}

public static Recruitment createRecruitment(
String name,
LocalDateTime startDate,
LocalDateTime endDate,
Integer academicYear,
SemesterType semesterType) {
SemesterType semesterType,
RoundType roundType,
Money fee) {
Period period = Period.createPeriod(startDate, endDate);
return Recruitment.builder()
.name(name)
.period(period)
.academicYear(academicYear)
.semesterType(semesterType)
.roundType(roundType)
.fee(fee)
.build();
}

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

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum RoundType {
FIRST("1차"),
SECOND("2차");

private final String value;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import static com.gdschongik.gdsc.global.common.constant.RegexConstant.*;

import com.gdschongik.gdsc.domain.common.model.SemesterType;
import com.gdschongik.gdsc.domain.recruitment.domain.RoundType;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDateTime;

public record RecruitmentCreateRequest(
Expand All @@ -15,4 +17,6 @@ public record RecruitmentCreateRequest(
@Future @Schema(description = "모집기간 종료일", pattern = DATETIME) LocalDateTime endDate,
@NotNull(message = "학년도는 null이 될 수 없습니다.") @Schema(description = "학년도", pattern = ACADEMIC_YEAR)
Integer academicYear,
@NotNull(message = "학기는 null이 될 수 없습니다.") @Schema(description = "학기") SemesterType semesterType) {}
@NotNull(message = "학기는 null이 될 수 없습니다.") @Schema(description = "학기") SemesterType semesterType,
@NotNull(message = "모집 차수는 null이 될 수 없습니다.") @Schema(description = "모집 차수") RoundType roundType,
@NotNull(message = "회비는 null이 될 수 없습니다.") @Schema(description = "회비") BigDecimal fee) {}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public enum ErrorCode {
RECRUITMENT_PERIOD_MISMATCH_SEMESTER_TYPE(HttpStatus.BAD_REQUEST, "모집 시작일과 종료일의 입력된 학기가 일치하지 않습니다."),
RECRUITMENT_PERIOD_SEMESTER_TYPE_UNMAPPED(HttpStatus.CONFLICT, "모집 시작일과 종료일이 매핑되는 학기가 없습니다."),
RECRUITMENT_PERIOD_NOT_WITHIN_TWO_WEEKS(HttpStatus.BAD_REQUEST, "모집 시작일과 종료일이 학기 시작일로부터 2주 이내에 있지 않습니다."),
RECRUITMENT_ROUND_TYPE_OVERLAP(HttpStatus.BAD_REQUEST, "모집 차수가 중복됩니다."),

// Coupon
COUPON_DISCOUNT_AMOUNT_NOT_POSITIVE(HttpStatus.CONFLICT, "쿠폰의 할인 금액은 0보다 커야 합니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ public Member createMember() {
}

private Recruitment createRecruitment() {
Recruitment recruitment =
Recruitment.createRecruitment(RECRUITMENT_NAME, START_DATE, END_DATE, ACADEMIC_YEAR, SEMESTER_TYPE);
Recruitment recruitment = Recruitment.createRecruitment(
RECRUITMENT_NAME, START_DATE, END_DATE, ACADEMIC_YEAR, SEMESTER_TYPE, ROUND_TYPE, FEE);
return recruitmentRepository.save(recruitment);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ class 멤버십_가입신청시 {
void 역할이_GUEST라면_멤버십_가입신청에_실패한다() {
// given
Member guestMember = Member.createGuestMember(OAUTH_ID);
Recruitment recruitment =
Recruitment.createRecruitment(RECRUITMENT_NAME, START_DATE, END_DATE, ACADEMIC_YEAR, SEMESTER_TYPE);
Recruitment recruitment = Recruitment.createRecruitment(
RECRUITMENT_NAME, START_DATE, END_DATE, ACADEMIC_YEAR, SEMESTER_TYPE, ROUND_TYPE, FEE);

// when & then
assertThatThrownBy(() -> Membership.createMembership(guestMember, recruitment))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ class AdminRecruitmentServiceTest extends IntegrationTest {
private RecruitmentRepository recruitmentRepository;

private void createRecruitment() {
Recruitment recruitment =
Recruitment.createRecruitment(RECRUITMENT_NAME, START_DATE, END_DATE, ACADEMIC_YEAR, SEMESTER_TYPE);
Recruitment recruitment = Recruitment.createRecruitment(
RECRUITMENT_NAME, START_DATE, END_DATE, ACADEMIC_YEAR, SEMESTER_TYPE, ROUND_TYPE, FEE);
recruitmentRepository.save(recruitment);
}

Expand All @@ -35,8 +35,8 @@ class 모집기간_생성시 {
void 기간이_중복되는_Recruitment가_있다면_실패한다() {
// given
createRecruitment();
RecruitmentCreateRequest request =
new RecruitmentCreateRequest(RECRUITMENT_NAME, START_DATE, END_DATE, ACADEMIC_YEAR, SEMESTER_TYPE);
RecruitmentCreateRequest request = new RecruitmentCreateRequest(
RECRUITMENT_NAME, START_DATE, END_DATE, ACADEMIC_YEAR, SEMESTER_TYPE, ROUND_TYPE, FEE_AMOUNT);

// when & then
assertThatThrownBy(() -> adminRecruitmentService.createRecruitment(request))
Expand All @@ -47,8 +47,8 @@ class 모집기간_생성시 {
@Test
void 모집_시작일과_종료일의_연도가_입력된_학년도와_다르다면_실패한다() {
// given
RecruitmentCreateRequest request =
new RecruitmentCreateRequest(RECRUITMENT_NAME, START_DATE, END_DATE, 2025, SEMESTER_TYPE);
RecruitmentCreateRequest request = new RecruitmentCreateRequest(
RECRUITMENT_NAME, START_DATE, END_DATE, 2025, SEMESTER_TYPE, ROUND_TYPE, FEE_AMOUNT);

// when & then
assertThatThrownBy(() -> adminRecruitmentService.createRecruitment(request))
Expand All @@ -60,7 +60,7 @@ class 모집기간_생성시 {
void 모집_시작일과_종료일의_학기가_입력된_학기와_다르다면_실패한다() {
// given
RecruitmentCreateRequest request = new RecruitmentCreateRequest(
RECRUITMENT_NAME, START_DATE, END_DATE, ACADEMIC_YEAR, SemesterType.SECOND);
RECRUITMENT_NAME, START_DATE, END_DATE, ACADEMIC_YEAR, SemesterType.SECOND, ROUND_TYPE, FEE_AMOUNT);

// when & then
assertThatThrownBy(() -> adminRecruitmentService.createRecruitment(request))
Expand All @@ -72,12 +72,37 @@ class 모집기간_생성시 {
void 모집_시작일과_종료일이_학기_시작일로부터_2주_이내에_있지_않다면_실패한다() {
// given
RecruitmentCreateRequest request = new RecruitmentCreateRequest(
RECRUITMENT_NAME, START_DATE, LocalDateTime.of(2024, 4, 10, 00, 00), ACADEMIC_YEAR, SEMESTER_TYPE);
RECRUITMENT_NAME,
START_DATE,
LocalDateTime.of(2024, 4, 10, 0, 0),
ACADEMIC_YEAR,
SEMESTER_TYPE,
ROUND_TYPE,
FEE_AMOUNT);

// when & then
assertThatThrownBy(() -> adminRecruitmentService.createRecruitment(request))
.isInstanceOf(CustomException.class)
.hasMessage(RECRUITMENT_PERIOD_NOT_WITHIN_TWO_WEEKS.getMessage());
}

@Test
void 학년도_학기_차수가_모두_중복되는_리쿠르팅이라면_실패한다() {
// given
createRecruitment();
RecruitmentCreateRequest request = new RecruitmentCreateRequest(
RECRUITMENT_NAME,
LocalDateTime.of(2024, 3, 12, 0, 0),
LocalDateTime.of(2024, 3, 13, 0, 0),
ACADEMIC_YEAR,
SEMESTER_TYPE,
ROUND_TYPE,
FEE_AMOUNT);

// when & then
assertThatThrownBy(() -> adminRecruitmentService.createRecruitment(request))
.isInstanceOf(CustomException.class)
.hasMessage(RECRUITMENT_ROUND_TYPE_OVERLAP.getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.gdschongik.gdsc.domain.recruitment.domain;

import static com.gdschongik.gdsc.global.common.constant.RecruitmentConstant.*;
import static com.gdschongik.gdsc.global.exception.ErrorCode.*;
import static org.assertj.core.api.Assertions.*;

import com.gdschongik.gdsc.domain.recruitment.domain.vo.Period;
Expand All @@ -18,8 +17,8 @@ class 학기생성시 {
Period period = Period.createPeriod(START_DATE, END_DATE);

// when
Recruitment recruitment =
Recruitment.createRecruitment(RECRUITMENT_NAME, START_DATE, END_DATE, ACADEMIC_YEAR, SEMESTER_TYPE);
Recruitment recruitment = Recruitment.createRecruitment(
RECRUITMENT_NAME, START_DATE, END_DATE, ACADEMIC_YEAR, SEMESTER_TYPE, ROUND_TYPE, FEE);

// then
assertThat(recruitment.getPeriod().getStartDate()).isEqualTo(START_DATE);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.gdschongik.gdsc.global.common.constant;

import com.gdschongik.gdsc.domain.common.model.SemesterType;
import com.gdschongik.gdsc.domain.common.vo.Money;
import com.gdschongik.gdsc.domain.recruitment.domain.RoundType;
import java.math.BigDecimal;
import java.time.LocalDateTime;

public class RecruitmentConstant {
Expand All @@ -10,6 +13,9 @@ public class RecruitmentConstant {
public static final LocalDateTime END_DATE = LocalDateTime.of(2024, 3, 11, 00, 00);
public static final Integer ACADEMIC_YEAR = 2024;
public static final SemesterType SEMESTER_TYPE = SemesterType.FIRST;
public static final Money FEE = Money.from(BigDecimal.valueOf(20000));
public static final BigDecimal FEE_AMOUNT = BigDecimal.valueOf(20000);
public static final RoundType ROUND_TYPE = RoundType.FIRST;

private RecruitmentConstant() {}
}