Skip to content

Commit f3c92be

Browse files
authored
Merge pull request #63 from cloudcomputinginha/dev
CHORE: Release v1.5
2 parents cee2da1 + 738af70 commit f3c92be

File tree

49 files changed

+455
-222
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+455
-222
lines changed

Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ ARG JAR_FILE=build/libs/*.jar
33
COPY ${JAR_FILE} app.jar
44
EXPOSE 8080
55
ENTRYPOINT ["java", "-jar", "/app.jar"]
6+
7+
RUN apk add tzdata && ln -snf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ dependencies {
3131
implementation 'org.springframework.boot:spring-boot-starter-web'
3232
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0'
3333
implementation 'software.amazon.awssdk:s3:2.21.5'
34-
implementation 'org.springframework.boot:spring-boot-starter-actuator'
34+
35+
//implementation 'org.springframework.boot:spring-boot-starter-actuator'
3536
compileOnly 'org.projectlombok:lombok'
3637
runtimeOnly 'com.mysql:mysql-connector-j'
3738
annotationProcessor 'org.projectlombok:lombok'

src/main/java/cloudcomputinginha/demo/apiPayload/code/status/ErrorStatus.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,13 @@ public enum ErrorStatus implements BaseErrorCode {
3131
RESUME_FILE_TYPE_INVALID(HttpStatus.BAD_REQUEST, "RESUME4003", "이력서는 PDF 파일만 업로드할 수 있습니다."),
3232
RESUME_FILE_NAME_INVALID(HttpStatus.BAD_REQUEST, "RESUME4004", "파일 이름은 한글, 영문, 숫자, 언더바(_), 대시(-), 공백만 허용하며 .pdf로 끝나야 합니다."),
3333

34-
// 자소서 + 이력서 관련
35-
AT_LEAST_ONE_PRESENT_DOCUMENTS(HttpStatus.BAD_REQUEST, "DOCUMENT4001", "이력서 또는 자기소개서 중 하나는 반드시 입력해야 합니다."),
36-
3734
// 면접 관련 에러
3835
INTERVIEW_NOT_FOUND(HttpStatus.NOT_FOUND, "INTERVIEW4001", "해당하는 인터뷰를 찾을 수 없습니다."),
3936
INTERVIEW_NOT_ACCEPTING_MEMBERS(HttpStatus.BAD_REQUEST, "INTERVIEW4002", "현재 인터뷰는 참여자를 받지 않습니다."),
4037
INTERVIEW_END_TIME_INVALID(HttpStatus.CONFLICT, "INTERVIEW4003", "인터뷰의 종료 시간이 시작 시간보다 빠릅니다."),
4138
INTERVIEW_ALREADY_TERMINATED(HttpStatus.CONFLICT, "INTERVIEW4004", "해당하는 인터뷰는 이미 종료되었습니다."),
4239
INTERVIEW_NO_PERMISSION(HttpStatus.FORBIDDEN, "INTERVIEW4005", "해당 인터뷰 수정 권한이 없습니다."),
40+
INTERVIEW_DOCUMENTS_NOT_FOUND(HttpStatus.BAD_REQUEST, "INTERVIEW4006", "이력서와 자기소개서는 필수입니다."),
4341

4442
// 멤버 인터뷰 관련 에러,
4543
INTERVIEW_STATUS_INVALID(HttpStatus.BAD_REQUEST, "MEMBERINTERVIEW4001", "올바른 인터뷰 상태가 아닙니다."),

src/main/java/cloudcomputinginha/demo/converter/CoverletterConverter.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22

33
import cloudcomputinginha.demo.domain.Coverletter;
44
import cloudcomputinginha.demo.domain.Member;
5-
import cloudcomputinginha.demo.domain.Qna;
65
import cloudcomputinginha.demo.web.dto.CoverletterRequestDTO;
76
import cloudcomputinginha.demo.web.dto.CoverletterResponseDTO;
8-
import cloudcomputinginha.demo.web.dto.QnaResponseDTO;
97

108
import java.util.List;
119

@@ -40,17 +38,17 @@ public static CoverletterResponseDTO.MyCoverletterListDTO toMyCoverletterListDTO
4038
.build();
4139
}
4240

43-
public static CoverletterResponseDTO.CoverletterDetailDTO toDetailDTO(Coverletter coverletter, List<Qna> qnas) {
44-
List<QnaResponseDTO.QnaDTO> qnaDTOs = qnas.stream()
45-
.map(QnaConverter::toQnaDTO)
46-
.toList();
47-
41+
public static CoverletterResponseDTO.CoverletterDetailDTO toDetailDTO(Coverletter coverletter) {
4842
return CoverletterResponseDTO.CoverletterDetailDTO.builder()
4943
.coverletterId(coverletter.getId())
5044
.corporateName(coverletter.getCorporateName())
5145
.jobName(coverletter.getJobName())
5246
.createdAt(coverletter.getCreatedAt())
53-
.qnaList(qnaDTOs)
47+
.qnaList(
48+
coverletter.getQnas().stream()
49+
.map(QnaConverter::toQnaDTO) //batch size 적용
50+
.toList()
51+
)
5452
.build();
5553
}
5654
}

src/main/java/cloudcomputinginha/demo/converter/InterviewConverter.java

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
package cloudcomputinginha.demo.converter;
22

3-
import cloudcomputinginha.demo.domain.*;
3+
import cloudcomputinginha.demo.domain.Interview;
4+
import cloudcomputinginha.demo.domain.InterviewOption;
5+
import cloudcomputinginha.demo.domain.Member;
6+
import cloudcomputinginha.demo.domain.MemberInterview;
47
import cloudcomputinginha.demo.domain.enums.StartType;
5-
import cloudcomputinginha.demo.web.dto.InterviewOptionResponseDTO;
68
import cloudcomputinginha.demo.web.dto.InterviewRequestDTO;
79
import cloudcomputinginha.demo.web.dto.InterviewResponseDTO;
8-
import cloudcomputinginha.demo.web.dto.MemberInterviewResponseDTO;
910

1011
import java.time.LocalDateTime;
1112
import java.util.List;
12-
import java.util.Map;
13-
import java.util.stream.Collectors;
1413

1514
public class InterviewConverter {
1615
public static InterviewResponseDTO.InterviewEndResultDTO toInterviewEndResultDTO(Interview interview) {
@@ -27,6 +26,7 @@ public static InterviewResponseDTO.InterviewCreateResultDTO createInterview(Inte
2726
.description(interview.getDescription())
2827
.corporateName(interview.getCorporateName())
2928
.jobName(interview.getJobName())
29+
.noticeUrl(interview.getNoticeUrl())
3030
.interviewFormat(interview.getInterviewOption().getInterviewFormat())
3131
.interviewType(interview.getInterviewOption().getInterviewType())
3232
.startType(interview.getStartType())
@@ -41,6 +41,7 @@ public static Interview toInterview(InterviewRequestDTO.InterviewCreateDTO reque
4141
.description(request.getDescription())
4242
.corporateName(request.getCorporateName())
4343
.jobName(request.getJobName())
44+
.noticeUrl(request.getNoticeUrl())
4445
.sessionName(request.getSessionName())
4546
.interviewOption(interviewOption)
4647
.startType(request.getStartType())
@@ -90,29 +91,21 @@ public static InterviewResponseDTO.InterviewDTO toInterviewDTO(Interview intervi
9091
.interviewId(interview.getId())
9192
.corporateName(interview.getCorporateName())
9293
.jobName(interview.getJobName())
94+
.noticeUrl(interview.getNoticeUrl())
9395
.startType(interview.getStartType())
9496
.participantCount(participantCount)
9597
.build();
9698
}
9799

98100
public static InterviewResponseDTO.InterviewStartResponseDTO toInterviewStartResponseDTO(
99101
Interview interview,
100-
List<MemberInterview> memberInterviews,
101-
List<Qna> allQnas
102+
List<MemberInterview> memberInterviews
102103
) {
103-
// 면접 참가자의 모든 QNA를 조회한 후, 자기소개서 id를 바탕으로 조회한 QNA를 그룹핑한다.
104-
Map<Long, List<Qna>> qnaMap = allQnas.stream()
105-
.collect(Collectors.groupingBy(q -> q.getCoverletter().getId()));
106-
107-
InterviewResponseDTO.InterviewDTO interviewDTO = InterviewConverter.toInterviewDTO(interview, memberInterviews.size());
108-
InterviewOptionResponseDTO.InterviewOptionDetailDTO interviewOptionDTO = InterviewOptionConverter.toInterviewOptionDTO(interview.getInterviewOption());
109-
List<MemberInterviewResponseDTO.ParticipantDTO> participantDTOs = MemberInterviewConverter.toParticipantDTOs(memberInterviews, qnaMap);
110-
111104
return InterviewResponseDTO.InterviewStartResponseDTO.builder()
112105
.interviewId(interview.getId())
113-
.interview(interviewDTO)
114-
.options(interviewOptionDTO)
115-
.participants(participantDTOs)
106+
.interview(InterviewConverter.toInterviewDTO(interview, memberInterviews.size()))
107+
.options(InterviewOptionConverter.toInterviewOptionDTO(interview.getInterviewOption()))
108+
.participants(MemberInterviewConverter.toParticipantDTOs(memberInterviews))
116109
.build();
117110
}
118111

@@ -124,6 +117,7 @@ public static InterviewResponseDTO.GroupInterviewDetailDTO toInterviewGroupDetai
124117
.description(interview.getDescription())
125118
.sessionName(interview.getSessionName())
126119
.jobName(interview.getJobName())
120+
.noticeUrl(interview.getNoticeUrl())
127121
.interviewType(interview.getInterviewOption().getInterviewType())
128122
.maxParticipants(interview.getMaxParticipants())
129123
.currentParticipants(interview.getCurrentParticipants().intValue())

src/main/java/cloudcomputinginha/demo/converter/MemberInterviewConverter.java

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package cloudcomputinginha.demo.converter;
22

33
import cloudcomputinginha.demo.domain.*;
4+
import cloudcomputinginha.demo.domain.enums.InterviewStatus;
45
import cloudcomputinginha.demo.web.dto.MemberInterviewResponseDTO;
56

67
import java.util.List;
7-
import java.util.Map;
88

99
public class MemberInterviewConverter {
1010
public static MemberInterviewResponseDTO.MemberInterviewStatusDTO toMemberInterviewStatusDTO(MemberInterview memberInterview) {
@@ -16,12 +16,19 @@ public static MemberInterviewResponseDTO.MemberInterviewStatusDTO toMemberInterv
1616
}
1717

1818
public static MemberInterview toMemberInterview(Member member, Interview interview, Resume resume, Coverletter coverletter) {
19-
return MemberInterview.builder()
20-
.member(member)
21-
.interview(interview)
19+
MemberInterview memberInterview = MemberInterview.builder()
2220
.resume(resume)
2321
.coverletter(coverletter)
22+
.status(InterviewStatus.SCHEDULED)
2423
.build();
24+
25+
memberInterview.setMember(member);
26+
memberInterview.setInterview(interview);
27+
28+
member.addMemberInterview(memberInterview);
29+
interview.addMemberInterview(memberInterview);
30+
31+
return memberInterview;
2532
}
2633

2734
public static MemberInterviewResponseDTO.CreateMemberInterviewDTO toMemberInterviewResultDTO(MemberInterview memberInterview) {
@@ -31,23 +38,18 @@ public static MemberInterviewResponseDTO.CreateMemberInterviewDTO toMemberInterv
3138
.build();
3239
}
3340

34-
public static MemberInterviewResponseDTO.ParticipantDTO toParticipantDTO(MemberInterview memberInterview, List<Qna> qnas) {
41+
public static MemberInterviewResponseDTO.ParticipantDTO toParticipantDTO(MemberInterview memberInterview) {
3542
return MemberInterviewResponseDTO.ParticipantDTO.builder()
3643
.memberInterviewId(memberInterview.getId())
3744
.resumeDTO(ResumeConverter.toResumeSimpleDTO(memberInterview.getResume()))
38-
.coverLetterDTO(CoverletterConverter.toDetailDTO(memberInterview.getCoverletter(), qnas))
45+
.coverLetterDTO(CoverletterConverter.toDetailDTO(memberInterview.getCoverletter()))
3946
.build();
4047
}
4148

42-
//참여자의 자기소개서 ID를 꺼내서 coverletterID에 해당하는 QNA 리스트를 가져온다.(해당 키가 없으면 빈 리스트 반환)
43-
//반환된 QNA 리스트와 참여자 정보를 기반으로 응답용 DTO 생성
44-
public static List<MemberInterviewResponseDTO.ParticipantDTO> toParticipantDTOs(List<MemberInterview> memberInterviews, Map<Long, List<Qna>> qnaMap) {
49+
public static List<MemberInterviewResponseDTO.ParticipantDTO> toParticipantDTOs(List<MemberInterview> memberInterviews) {
4550
return memberInterviews.stream()
46-
.map(mi -> {
47-
Long coverletterId = mi.getCoverletter().getId();
48-
List<Qna> qnas = qnaMap.getOrDefault(coverletterId, List.of());
49-
return MemberInterviewConverter.toParticipantDTO(mi, qnas);
50-
}).toList();
51+
.map(MemberInterviewConverter::toParticipantDTO)
52+
.toList();
5153
}
5254

5355
public static MemberInterviewResponseDTO.MyInterviewListDTO toMyInterviewListDTO(List<MemberInterview> myInterviews) {

src/main/java/cloudcomputinginha/demo/converter/ResumeConverter.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public static ResumeResponseDTO.ResumeListDTO toResumeListDTO(List<Resume> resum
5252
public static ResumeResponseDTO.ResumeSimpleDTO toResumeSimpleDTO(Resume resume) {
5353
return ResumeResponseDTO.ResumeSimpleDTO.builder()
5454
.resumeId(resume.getId())
55+
.fileName(resume.getFileName())
5556
.fileUrl(resume.getFileUrl())
5657
.build();
5758
}

src/main/java/cloudcomputinginha/demo/domain/Coverletter.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
import cloudcomputinginha.demo.domain.common.BaseEntity;
66
import jakarta.persistence.*;
77
import lombok.*;
8+
import org.hibernate.annotations.BatchSize;
9+
10+
import java.util.ArrayList;
11+
import java.util.List;
812

913
@Entity
1014
@Getter
@@ -27,9 +31,20 @@ public class Coverletter extends BaseEntity {
2731
@Column(length = 20)
2832
private String jobName;
2933

34+
// 자소서 삭제 시 qna도 함께 삭제되길 원함.
35+
@Builder.Default
36+
@BatchSize(size = 50) // Lazy loading 시 성능 최적화를 위해 배치 사이즈 설정
37+
@OneToMany(mappedBy = "coverletter", cascade = CascadeType.ALL, orphanRemoval = true)
38+
private List<Qna> qnas = new ArrayList<>();
39+
3040
public void validateOwnedBy(Long memberId) {
3141
if (!this.member.getId().equals(memberId)) {
3242
throw new CoverletterHandler(ErrorStatus.COVERLETTER_NOT_OWNED);
3343
}
3444
}
45+
46+
public void addQna(Qna qna) {
47+
this.qnas.add(qna);
48+
qna.setCoverletter(this);
49+
}
3550
}

src/main/java/cloudcomputinginha/demo/domain/Interview.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@
33
import cloudcomputinginha.demo.apiPayload.code.handler.MemberInterviewHandler;
44
import cloudcomputinginha.demo.apiPayload.code.status.ErrorStatus;
55
import cloudcomputinginha.demo.domain.common.BaseEntity;
6+
import cloudcomputinginha.demo.domain.embedded.Url;
67
import cloudcomputinginha.demo.domain.enums.StartType;
78
import jakarta.persistence.*;
89
import lombok.*;
910
import org.hibernate.annotations.DynamicInsert;
1011
import org.hibernate.annotations.DynamicUpdate;
1112

1213
import java.time.LocalDateTime;
14+
import java.util.ArrayList;
15+
import java.util.List;
1316

1417
@Entity
1518
@Getter
@@ -51,6 +54,9 @@ public class Interview extends BaseEntity {
5154
@Builder.Default
5255
private Integer maxParticipants = 1; //일대일 면접을 기준으로 초기화
5356

57+
@Column(length = 255)
58+
private String noticeUrl; //면접 채용공고 URL
59+
5460
@Builder.Default
5561
private Boolean isOpen = false;
5662

@@ -62,6 +68,21 @@ public class Interview extends BaseEntity {
6268
@JoinColumn(name = "interview_option_id", unique = true, nullable = false)
6369
private InterviewOption interviewOption;
6470

71+
// 면접 삭제 시 참여자 기록도 삭제
72+
@Builder.Default
73+
@OneToMany(mappedBy = "interview", cascade = CascadeType.ALL, orphanRemoval = true)
74+
private List<MemberInterview> memberInterviews = new ArrayList<>();
75+
76+
public void addMemberInterview(MemberInterview memberInterview) {
77+
memberInterviews.add(memberInterview);
78+
// memberInterview.setInterview(this);
79+
}
80+
81+
public void setInterviewOption(InterviewOption option) {
82+
this.interviewOption = option;
83+
option.setInterview(this);
84+
}
85+
6586
public void updateStartedAt(LocalDateTime startedAt) {
6687
this.startedAt = startedAt;
6788
}

src/main/java/cloudcomputinginha/demo/domain/InterviewOption.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,13 @@ public class InterviewOption extends BaseEntity {
4040

4141
private Integer answerTime;
4242

43-
@OneToOne(mappedBy = "interviewOption", cascade = CascadeType.ALL)
43+
@OneToOne(mappedBy = "interviewOption")
4444
private Interview interview;
4545

46+
public void setInterview(Interview interview) {
47+
this.interview = interview;
48+
}
49+
4650
public void updateVoiceType(VoiceType voiceType) {
4751
this.voiceType = voiceType;
4852
}

0 commit comments

Comments
 (0)