Skip to content

Commit 94eb07c

Browse files
committed
feat: 면접 예약 API 구현
1 parent 1c00f30 commit 94eb07c

17 files changed

+568
-1
lines changed

src/main/java/dmu/dasom/api/domain/common/exception/ErrorCode.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,14 @@ public enum ErrorCode {
2626
INVALID_TIME_FORMAT(400, "C017", "시간 형식이 올바르지 않습니다."),
2727
INVALID_INQUIRY_PERIOD(400, "C018", "조회 기간이 아닙니다."),
2828
SHEET_WRITE_FAIL(400, "C019", "시트에 데이터를 쓰는데 실패하였습니다."),
29-
SHEET_READ_FAIL(400, "C200", "시트에 데이터를 쓰는데 실패하였습니다."),
29+
SHEET_READ_FAIL(400, "C200", "시트에 데이터를 읽는데 실패하였습니다."),
30+
SLOT_NOT_FOUND(400, "C021", "슬롯을 찾을 수 없습니다."),
31+
APPLICANT_NOT_FOUND(400, "C022", "지원자를 찾을 수 없습니다."),
32+
ALREADY_RESERVED(400, "C023", "이미 예약된 지원자입니다."),
33+
RESERVED_SLOT_CANNOT_BE_DELETED(400, "C024", "예약된 슬롯은 삭제할 수 없습니다."),
34+
SLOT_FULL(400, "C025", "해당 슬롯이 가득 찼습니다."),
35+
RESERVATION_NOT_FOUND(400, "C026", "예약을 찾을 수 없습니다."),
36+
SLOT_NOT_ACTIVE(400, "C027", "해당 슬롯이 비활성화 되었습니다."),
3037
;
3138

3239
private final int status;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package dmu.dasom.api.domain.interview.dto;
2+
3+
import lombok.*;
4+
5+
@Getter
6+
@Setter
7+
@NoArgsConstructor
8+
@AllArgsConstructor
9+
@Builder
10+
public class InterviewReservationRequestDto {
11+
12+
private Long slotId; // 예약할 슬롯 ID
13+
14+
private Long applicantId; // 지원자 ID
15+
16+
private String reservationCode; // 학번 뒤 4자리 + 전화번호 뒤 4자리 조합 코드
17+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package dmu.dasom.api.domain.interview.dto;
2+
3+
import lombok.*;
4+
5+
@Getter
6+
@Setter
7+
@NoArgsConstructor
8+
@AllArgsConstructor
9+
@Builder
10+
public class InterviewReservationResponseDto {
11+
12+
private Long reservationId; // 예약 ID
13+
14+
private Long slotId; // 슬롯 ID
15+
16+
private Long applicantId; // 지원자 ID
17+
18+
private String reservationCode; // 예약 코드 (학번+전화번호 조합)
19+
20+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package dmu.dasom.api.domain.interview.dto;
2+
3+
import lombok.*;
4+
5+
import java.time.LocalDate;
6+
import java.time.LocalTime;
7+
8+
@Getter
9+
@Setter
10+
@NoArgsConstructor
11+
@AllArgsConstructor
12+
@Builder
13+
public class InterviewSlotRequestDto {
14+
15+
private LocalDate interviewDate; // 면접 날짜
16+
17+
private LocalTime startTime; // 시작 시간
18+
19+
private LocalTime endTime; // 종료 시간
20+
21+
private Integer maxCandidates; // 최대 지원자 수
22+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package dmu.dasom.api.domain.interview.dto;
2+
3+
import dmu.dasom.api.domain.interview.entity.InterviewSlot;
4+
import lombok.*;
5+
6+
import java.time.LocalDate;
7+
import java.time.LocalTime;
8+
9+
@Getter
10+
@Setter
11+
@NoArgsConstructor
12+
@AllArgsConstructor
13+
@Builder
14+
public class InterviewSlotResponseDto {
15+
16+
private Long id; // 슬롯 ID
17+
private LocalDate interviewDate; // 면접 날짜
18+
private LocalTime startTime; // 시작 시간
19+
private LocalTime endTime; // 종료 시간
20+
private Integer maxCandidates; // 최대 지원자 수
21+
private Integer currentCandidates; // 현재 예약된 지원자 수
22+
23+
public InterviewSlotResponseDto(InterviewSlot slot){
24+
this.id = slot.getId();
25+
this.interviewDate = slot.getInterviewDate();
26+
this.startTime = slot.getStartTime();
27+
this.endTime = slot.getEndTime();
28+
this.maxCandidates = slot.getMaxCandidates();
29+
this.currentCandidates = slot.getCurrentCandidates();
30+
}
31+
32+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package dmu.dasom.api.domain.interview.entity;
2+
3+
import dmu.dasom.api.domain.applicant.entity.Applicant;
4+
import jakarta.persistence.*;
5+
import lombok.*;
6+
7+
@Entity
8+
@Getter
9+
@Setter
10+
@NoArgsConstructor
11+
@AllArgsConstructor
12+
@Builder
13+
public class InterviewReservation {
14+
15+
@Id
16+
@GeneratedValue(strategy = GenerationType.IDENTITY)
17+
private Long id;
18+
19+
@ManyToOne(fetch = FetchType.LAZY)
20+
@JoinColumn(name = "slot_id", nullable = false)
21+
private InterviewSlot slot; // 연관된 면접 슬롯
22+
23+
@ManyToOne(fetch = FetchType.LAZY)
24+
@JoinColumn(name = "applicant_id", nullable = false)
25+
private Applicant applicant; // 지원자
26+
27+
@Column(nullable = false, unique = true, length = 8)
28+
private String reservationCode; // 학번 뒤 4자리 + 전화번호 뒤 4자리 조합 코드
29+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package dmu.dasom.api.domain.interview.entity;
2+
3+
import dmu.dasom.api.domain.interview.enums.Status;
4+
import jakarta.persistence.*;
5+
import lombok.*;
6+
7+
import java.time.LocalDate;
8+
import java.time.LocalTime;
9+
10+
@Entity
11+
@Getter
12+
@Setter
13+
@NoArgsConstructor
14+
@AllArgsConstructor
15+
@Builder
16+
public class InterviewSlot {
17+
@Id
18+
@GeneratedValue(strategy = GenerationType.IDENTITY)
19+
private Long id;
20+
21+
@Column(nullable = false)
22+
private LocalDate interviewDate;
23+
24+
@Column(nullable = false)
25+
private LocalTime startTime;
26+
27+
@Column(nullable = false)
28+
private LocalTime endTime; // 종료 시간
29+
30+
@Column(nullable = false)
31+
private Integer maxCandidates; // 최대 지원자 수
32+
33+
@Column(nullable = false)
34+
private Integer currentCandidates; // 현재 예약된 지원자 수
35+
36+
@Enumerated(EnumType.STRING)
37+
@Column(nullable = false, length = 16)
38+
private Status status; // 면접 슬롯 상태 (ACTIVE, INACTIVE, CLOSED)
39+
40+
public void incrementCurrentCandidates() {
41+
this.currentCandidates++;
42+
if (this.currentCandidates >= this.maxCandidates) {
43+
this.status = Status.CLOSED; // 최대 지원자 수에 도달하면 상태 변경
44+
}
45+
}
46+
47+
public void decrementCurrentCandidates() {
48+
this.currentCandidates--;
49+
if (status == Status.CLOSED && this.currentCandidates < this.maxCandidates) {
50+
this.status = Status.ACTIVE; // 지원자 수가 줄어들면 다시 활성화
51+
}
52+
}
53+
54+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package dmu.dasom.api.domain.interview.enums;
2+
3+
public enum Status {
4+
ACTIVE, // 활성화된 슬롯
5+
INACTIVE, // 비활성화된 슬롯
6+
CLOSED // 예약 마감된 슬롯
7+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package dmu.dasom.api.domain.interview.repositoty;
2+
3+
import dmu.dasom.api.domain.interview.entity.InterviewReservation;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
import org.springframework.stereotype.Repository;
6+
7+
@Repository
8+
public interface InterviewReservationRepository extends JpaRepository<InterviewReservation, Long> {
9+
boolean existsByReservationCode(String reservationCode);
10+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package dmu.dasom.api.domain.interview.repositoty;
2+
3+
import dmu.dasom.api.domain.interview.entity.InterviewSlot;
4+
import dmu.dasom.api.domain.interview.enums.Status;
5+
import org.springframework.data.jpa.repository.JpaRepository;
6+
import org.springframework.stereotype.Repository;
7+
8+
import java.util.Collection;
9+
import java.util.List;
10+
11+
@Repository
12+
public interface InterviewSlotRepository extends JpaRepository<InterviewSlot, Long> {
13+
Collection<InterviewSlot> findAllByCurrentCandidatesLessThanMaxCandidates();
14+
List<InterviewSlot> findAllByStatusAndCurrentCandidatesLessThanMaxCandidates(
15+
Status status);
16+
17+
boolean exists();
18+
}

0 commit comments

Comments
 (0)