Skip to content

Commit cb612ba

Browse files
authored
feat: 모집 일정 관리 API 구현
* feat: 모집일정 관리 Entity 추가 - key:value 형태로 저장 * feat: 모집 일정 관리 key 정의 * feat: 모집 일정 응답, 수정 요청 DTO 구현 * feat: Recruit Repository 구현 * feat: 모집 일정 조회, 설정 수정 로직 구현 * feat: 모집 일정 관련 에러 코드 추가 * feat: 모집 일정 조회 엔드포인트 추가 * feat: 모집 일정 수정 엔드포인트 추가 - AdminController의 누락된 swagger 명세 추가 * test: Recruit 테스트 케이스 작성 * fix: 병합 충돌 해결 - 에러 코드 중복 회피 * fix: 병합 충돌 해결, 코드 포맷팅
1 parent 2310796 commit cb612ba

File tree

11 files changed

+424
-49
lines changed

11 files changed

+424
-49
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ public enum ErrorCode {
2121
EMPTY_RESULT(400, "C012", "조회 결과가 없습니다."),
2222
DUPLICATED_STUDENT_NO(400, "C013", "이미 등록된 학번입니다."),
2323
SEND_EMAIL_FAIL(400, "C014", "이메일 전송에 실패하였습니다."),
24-
MAIL_TYPE_NOT_VALID(400, "C015", "메일 타입이 올바르지 않습니다.")
24+
MAIL_TYPE_NOT_VALID(400, "C015", "메일 타입이 올바르지 않습니다."),
25+
INVALID_DATETIME_FORMAT(400, "C016", "날짜 형식이 올바르지 않습니다."),
26+
INVALID_TIME_FORMAT(400, "C017", "시간 형식이 올바르지 않습니다.")
2527
;
2628

2729
private final int status;

src/main/java/dmu/dasom/api/domain/recruit/controller/RecruitController.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import dmu.dasom.api.domain.applicant.dto.ApplicantCreateRequestDto;
44
import dmu.dasom.api.domain.applicant.service.ApplicantService;
55
import dmu.dasom.api.domain.common.exception.ErrorResponse;
6+
import dmu.dasom.api.domain.recruit.dto.RecruitConfigResponseDto;
7+
import dmu.dasom.api.domain.recruit.service.RecruitService;
68
import io.swagger.v3.oas.annotations.Operation;
79
import io.swagger.v3.oas.annotations.media.Content;
810
import io.swagger.v3.oas.annotations.media.ExampleObject;
@@ -12,17 +14,17 @@
1214
import jakarta.validation.Valid;
1315
import lombok.RequiredArgsConstructor;
1416
import org.springframework.http.ResponseEntity;
15-
import org.springframework.web.bind.annotation.PostMapping;
16-
import org.springframework.web.bind.annotation.RequestBody;
17-
import org.springframework.web.bind.annotation.RequestMapping;
18-
import org.springframework.web.bind.annotation.RestController;
17+
import org.springframework.web.bind.annotation.*;
18+
19+
import java.util.List;
1920

2021
@RestController
2122
@RequestMapping("/api/recruit")
2223
@RequiredArgsConstructor
2324
public class RecruitController {
2425

2526
private final ApplicantService applicantService;
27+
private final RecruitService recruitService;
2628

2729
// 지원하기
2830
@Operation(summary = "부원 지원하기")
@@ -42,4 +44,12 @@ public ResponseEntity<Void> apply(@Valid @RequestBody final ApplicantCreateReque
4244
return ResponseEntity.ok().build();
4345
}
4446

47+
// 모집 일정 조회
48+
@Operation(summary = "모집 일정 조회")
49+
@ApiResponse(responseCode = "200", description = "모집 일정 조회 성공")
50+
@GetMapping
51+
public ResponseEntity<List<RecruitConfigResponseDto>> getRecruitSchedule() {
52+
return ResponseEntity.ok(recruitService.getRecruitSchedule());
53+
}
54+
4555
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package dmu.dasom.api.domain.recruit.dto;
2+
3+
import dmu.dasom.api.domain.recruit.enums.ConfigKey;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
import lombok.Builder;
6+
import lombok.Getter;
7+
8+
@Getter
9+
@Builder
10+
@Schema(name = "RecruitConfigResponseDto", description = "모집 설정 응답 DTO")
11+
public class RecruitConfigResponseDto {
12+
13+
@Schema(description = "모집 설정 키", example = "RECRUITMENT_PERIOD_START")
14+
private ConfigKey key;
15+
16+
@Schema(description = "모집 설정 값 (날짜 혹은 시간 값)", example = "2024-10-01T10:00:00 || 10:00:00")
17+
private String value;
18+
19+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package dmu.dasom.api.domain.recruit.dto;
2+
3+
import dmu.dasom.api.domain.recruit.enums.ConfigKey;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
import jakarta.validation.constraints.NotNull;
6+
import lombok.Getter;
7+
8+
@Getter
9+
@Schema(name = "RecruitScheduleModifyRequestDto", description = "모집 일정 수정 요청 DTO")
10+
public class RecruitScheduleModifyRequestDto {
11+
12+
@NotNull
13+
@Schema(description = "모집 설정 키", example = "key Enum 확인")
14+
ConfigKey key;
15+
16+
@NotNull
17+
@Schema(description = "모집 설정 값 (날짜 혹은 시간 값)", example = "2024-10-01T10:00:00 || 10:00:00")
18+
String value;
19+
20+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package dmu.dasom.api.domain.recruit.entity;
2+
3+
import dmu.dasom.api.domain.recruit.dto.RecruitConfigResponseDto;
4+
import dmu.dasom.api.domain.recruit.enums.ConfigKey;
5+
import jakarta.persistence.*;
6+
import lombok.*;
7+
import org.hibernate.annotations.DynamicUpdate;
8+
9+
import java.time.LocalDateTime;
10+
import java.time.LocalTime;
11+
import java.time.format.DateTimeFormatter;
12+
13+
@AllArgsConstructor
14+
@Builder
15+
@DynamicUpdate
16+
@Entity
17+
@Getter
18+
@NoArgsConstructor
19+
public class Recruit {
20+
21+
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
22+
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
23+
24+
@Enumerated(EnumType.STRING)
25+
@Column(name = "key", length = 64, nullable = false, unique = true)
26+
@Id
27+
private ConfigKey key;
28+
29+
@Column(name = "value")
30+
private String value;
31+
32+
public void updateDateTime(final LocalDateTime dateTime) {
33+
this.value = dateTime.format(DATE_TIME_FORMATTER);
34+
}
35+
36+
public void updateTime(final LocalTime time) {
37+
this.value = time.format(TIME_FORMATTER);
38+
}
39+
40+
public RecruitConfigResponseDto toResponse() {
41+
LocalDateTime dateTime = LocalDateTime.parse(this.value, DATE_TIME_FORMATTER);
42+
return RecruitConfigResponseDto.builder()
43+
.key(key)
44+
.value(dateTime.format(DATE_TIME_FORMATTER))
45+
.build();
46+
}
47+
48+
public RecruitConfigResponseDto toTimeResponse() {
49+
LocalTime time = LocalTime.parse(this.value, TIME_FORMATTER);
50+
return RecruitConfigResponseDto.builder()
51+
.key(key)
52+
.value(time.format(TIME_FORMATTER))
53+
.build();
54+
}
55+
56+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package dmu.dasom.api.domain.recruit.enums;
2+
3+
public enum ConfigKey {
4+
5+
// 모집 기간(시작일, 종료일 - 시간 포함)
6+
RECRUITMENT_PERIOD_START, // 모집 시작일
7+
RECRUITMENT_PERIOD_END, // 모집 종료일
8+
9+
// 1차 합격 발표일 (시간 포함)
10+
DOCUMENT_PASS_ANNOUNCEMENT, // 1차 합격 발표일
11+
12+
// 면접 기간 (시작일, 종료일)
13+
INTERVIEW_PERIOD_START, // 면접 기간 시작일
14+
INTERVIEW_PERIOD_END, // 면접 기간 종료일
15+
16+
// 면접 시간 (시작 시간, 종료 시간)
17+
INTERVIEW_TIME_START, // 면접 시작 시간
18+
INTERVIEW_TIME_END, // 면접 종료 시간
19+
20+
// 2차 합격 발표일 (시간 포함)
21+
INTERVIEW_PASS_ANNOUNCEMENT // 2차 합격 발표일
22+
23+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package dmu.dasom.api.domain.recruit.repository;
2+
3+
import dmu.dasom.api.domain.recruit.entity.Recruit;
4+
import dmu.dasom.api.domain.recruit.enums.ConfigKey;
5+
import org.springframework.data.jpa.repository.JpaRepository;
6+
7+
import java.util.Optional;
8+
9+
public interface RecruitRepository extends JpaRepository<Recruit, Long> {
10+
11+
Optional<Recruit> findByKey(final ConfigKey key);
12+
13+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package dmu.dasom.api.domain.recruit.service;
2+
3+
import dmu.dasom.api.domain.recruit.dto.RecruitConfigResponseDto;
4+
import dmu.dasom.api.domain.recruit.dto.RecruitScheduleModifyRequestDto;
5+
6+
import java.util.List;
7+
8+
public interface RecruitService {
9+
10+
List<RecruitConfigResponseDto> getRecruitSchedule();
11+
12+
void modifyRecruitSchedule(final RecruitScheduleModifyRequestDto requestDto);
13+
14+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package dmu.dasom.api.domain.recruit.service;
2+
3+
import dmu.dasom.api.domain.common.exception.CustomException;
4+
import dmu.dasom.api.domain.common.exception.ErrorCode;
5+
import dmu.dasom.api.domain.recruit.dto.RecruitConfigResponseDto;
6+
import dmu.dasom.api.domain.recruit.dto.RecruitScheduleModifyRequestDto;
7+
import dmu.dasom.api.domain.recruit.entity.Recruit;
8+
import dmu.dasom.api.domain.recruit.enums.ConfigKey;
9+
import dmu.dasom.api.domain.recruit.repository.RecruitRepository;
10+
import lombok.RequiredArgsConstructor;
11+
import org.springframework.stereotype.Service;
12+
import org.springframework.transaction.annotation.Transactional;
13+
14+
import java.time.LocalDateTime;
15+
import java.time.LocalTime;
16+
import java.time.format.DateTimeFormatter;
17+
import java.time.format.DateTimeParseException;
18+
import java.util.List;
19+
20+
@RequiredArgsConstructor
21+
@Service
22+
@Transactional(readOnly = true)
23+
public class RecruitServiceImpl implements RecruitService {
24+
25+
private final RecruitRepository recruitRepository;
26+
27+
// 모집 일정 설정 조회
28+
@Override
29+
public List<RecruitConfigResponseDto> getRecruitSchedule() {
30+
return findAll().stream()
31+
.map(config -> config.getKey() == ConfigKey.INTERVIEW_TIME_START || config.getKey() == ConfigKey.INTERVIEW_TIME_END
32+
? config.toTimeResponse() : config.toResponse())
33+
.toList();
34+
}
35+
36+
// 모집 일정 설정 수정
37+
@Override
38+
@Transactional
39+
public void modifyRecruitSchedule(final RecruitScheduleModifyRequestDto request) {
40+
final Recruit config = findByKey(request.getKey());
41+
42+
if (request.getKey().equals(ConfigKey.INTERVIEW_TIME_START) || request.getKey().equals(ConfigKey.INTERVIEW_TIME_END)) {
43+
final LocalTime time = parseTimeFormat(request.getValue());
44+
config.updateTime(time);
45+
return;
46+
}
47+
48+
final LocalDateTime dateTime = parseDateTimeFormat(request.getValue());
49+
config.updateDateTime(dateTime);
50+
}
51+
52+
// DB에 저장된 모든 Recruit 객체를 찾아 반환
53+
private List<Recruit> findAll() {
54+
return recruitRepository.findAll();
55+
}
56+
57+
// DB에서 key에 해당하는 Recruit 객체를 찾아 반환
58+
private Recruit findByKey(final ConfigKey key) {
59+
return recruitRepository.findByKey(key)
60+
.orElseThrow(() -> new CustomException(ErrorCode.ARGUMENT_NOT_VALID));
61+
62+
}
63+
64+
// 시간 형식 변환 및 검증
65+
private LocalTime parseTimeFormat(String value) {
66+
try {
67+
return LocalTime.parse(value, DateTimeFormatter.ofPattern("HH:mm:ss"));
68+
} catch (DateTimeParseException e) {
69+
throw new CustomException(ErrorCode.INVALID_TIME_FORMAT);
70+
}
71+
}
72+
73+
// 날짜 및 시간 형식 변환 및 검증
74+
private LocalDateTime parseDateTimeFormat(String value) {
75+
try {
76+
return LocalDateTime.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"));
77+
} catch (DateTimeParseException e) {
78+
throw new CustomException(ErrorCode.INVALID_DATETIME_FORMAT);
79+
}
80+
}
81+
82+
}

0 commit comments

Comments
 (0)